Roo/grid/GridView.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) {
4462             val = 0;
4463         }
4464         return val;
4465     },
4466     
4467     /**
4468      * Integer sorting
4469      * @param {Mixed} s The value being converted
4470      * @return {Number} The comparison value
4471      */
4472     asInt : function(s) {
4473         var val = parseInt(String(s).replace(/,/g, ""));
4474         if(isNaN(val)) {
4475             val = 0;
4476         }
4477         return val;
4478     }
4479 };/*
4480  * Based on:
4481  * Ext JS Library 1.1.1
4482  * Copyright(c) 2006-2007, Ext JS, LLC.
4483  *
4484  * Originally Released Under LGPL - original licence link has changed is not relivant.
4485  *
4486  * Fork - LGPL
4487  * <script type="text/javascript">
4488  */
4489
4490 /**
4491 * @class Roo.data.Record
4492  * Instances of this class encapsulate both record <em>definition</em> information, and record
4493  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4494  * to access Records cached in an {@link Roo.data.Store} object.<br>
4495  * <p>
4496  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4497  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4498  * objects.<br>
4499  * <p>
4500  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4501  * @constructor
4502  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4503  * {@link #create}. The parameters are the same.
4504  * @param {Array} data An associative Array of data values keyed by the field name.
4505  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4506  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4507  * not specified an integer id is generated.
4508  */
4509 Roo.data.Record = function(data, id){
4510     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4511     this.data = data;
4512 };
4513
4514 /**
4515  * Generate a constructor for a specific record layout.
4516  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4517  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4518  * Each field definition object may contain the following properties: <ul>
4519  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4520  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4521  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4522  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4523  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4524  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4525  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4526  * this may be omitted.</p></li>
4527  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4528  * <ul><li>auto (Default, implies no conversion)</li>
4529  * <li>string</li>
4530  * <li>int</li>
4531  * <li>float</li>
4532  * <li>boolean</li>
4533  * <li>date</li></ul></p></li>
4534  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4535  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4536  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4537  * by the Reader into an object that will be stored in the Record. It is passed the
4538  * following parameters:<ul>
4539  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4540  * </ul></p></li>
4541  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4542  * </ul>
4543  * <br>usage:<br><pre><code>
4544 var TopicRecord = Roo.data.Record.create(
4545     {name: 'title', mapping: 'topic_title'},
4546     {name: 'author', mapping: 'username'},
4547     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4548     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4549     {name: 'lastPoster', mapping: 'user2'},
4550     {name: 'excerpt', mapping: 'post_text'}
4551 );
4552
4553 var myNewRecord = new TopicRecord({
4554     title: 'Do my job please',
4555     author: 'noobie',
4556     totalPosts: 1,
4557     lastPost: new Date(),
4558     lastPoster: 'Animal',
4559     excerpt: 'No way dude!'
4560 });
4561 myStore.add(myNewRecord);
4562 </code></pre>
4563  * @method create
4564  * @static
4565  */
4566 Roo.data.Record.create = function(o){
4567     var f = function(){
4568         f.superclass.constructor.apply(this, arguments);
4569     };
4570     Roo.extend(f, Roo.data.Record);
4571     var p = f.prototype;
4572     p.fields = new Roo.util.MixedCollection(false, function(field){
4573         return field.name;
4574     });
4575     for(var i = 0, len = o.length; i < len; i++){
4576         p.fields.add(new Roo.data.Field(o[i]));
4577     }
4578     f.getField = function(name){
4579         return p.fields.get(name);  
4580     };
4581     return f;
4582 };
4583
4584 Roo.data.Record.AUTO_ID = 1000;
4585 Roo.data.Record.EDIT = 'edit';
4586 Roo.data.Record.REJECT = 'reject';
4587 Roo.data.Record.COMMIT = 'commit';
4588
4589 Roo.data.Record.prototype = {
4590     /**
4591      * Readonly flag - true if this record has been modified.
4592      * @type Boolean
4593      */
4594     dirty : false,
4595     editing : false,
4596     error: null,
4597     modified: null,
4598
4599     // private
4600     join : function(store){
4601         this.store = store;
4602     },
4603
4604     /**
4605      * Set the named field to the specified value.
4606      * @param {String} name The name of the field to set.
4607      * @param {Object} value The value to set the field to.
4608      */
4609     set : function(name, value){
4610         if(this.data[name] == value){
4611             return;
4612         }
4613         this.dirty = true;
4614         if(!this.modified){
4615             this.modified = {};
4616         }
4617         if(typeof this.modified[name] == 'undefined'){
4618             this.modified[name] = this.data[name];
4619         }
4620         this.data[name] = value;
4621         if(!this.editing && this.store){
4622             this.store.afterEdit(this);
4623         }       
4624     },
4625
4626     /**
4627      * Get the value of the named field.
4628      * @param {String} name The name of the field to get the value of.
4629      * @return {Object} The value of the field.
4630      */
4631     get : function(name){
4632         return this.data[name]; 
4633     },
4634
4635     // private
4636     beginEdit : function(){
4637         this.editing = true;
4638         this.modified = {}; 
4639     },
4640
4641     // private
4642     cancelEdit : function(){
4643         this.editing = false;
4644         delete this.modified;
4645     },
4646
4647     // private
4648     endEdit : function(){
4649         this.editing = false;
4650         if(this.dirty && this.store){
4651             this.store.afterEdit(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Rejects all changes made to the Record since either creation, or the last commit operation.
4658      * Modified fields are reverted to their original values.
4659      * <p>
4660      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4661      * of reject operations.
4662      */
4663     reject : function(){
4664         var m = this.modified;
4665         for(var n in m){
4666             if(typeof m[n] != "function"){
4667                 this.data[n] = m[n];
4668             }
4669         }
4670         this.dirty = false;
4671         delete this.modified;
4672         this.editing = false;
4673         if(this.store){
4674             this.store.afterReject(this);
4675         }
4676     },
4677
4678     /**
4679      * Usually called by the {@link Roo.data.Store} which owns the Record.
4680      * Commits all changes made to the Record since either creation, or the last commit operation.
4681      * <p>
4682      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4683      * of commit operations.
4684      */
4685     commit : function(){
4686         this.dirty = false;
4687         delete this.modified;
4688         this.editing = false;
4689         if(this.store){
4690             this.store.afterCommit(this);
4691         }
4692     },
4693
4694     // private
4695     hasError : function(){
4696         return this.error != null;
4697     },
4698
4699     // private
4700     clearError : function(){
4701         this.error = null;
4702     },
4703
4704     /**
4705      * Creates a copy of this record.
4706      * @param {String} id (optional) A new record id if you don't want to use this record's id
4707      * @return {Record}
4708      */
4709     copy : function(newId) {
4710         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4711     }
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722
4723
4724
4725 /**
4726  * @class Roo.data.Store
4727  * @extends Roo.util.Observable
4728  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4729  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4730  * <p>
4731  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4732  * has no knowledge of the format of the data returned by the Proxy.<br>
4733  * <p>
4734  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4735  * instances from the data object. These records are cached and made available through accessor functions.
4736  * @constructor
4737  * Creates a new Store.
4738  * @param {Object} config A config object containing the objects needed for the Store to access data,
4739  * and read the data into Records.
4740  */
4741 Roo.data.Store = function(config){
4742     this.data = new Roo.util.MixedCollection(false);
4743     this.data.getKey = function(o){
4744         return o.id;
4745     };
4746     this.baseParams = {};
4747     // private
4748     this.paramNames = {
4749         "start" : "start",
4750         "limit" : "limit",
4751         "sort" : "sort",
4752         "dir" : "dir",
4753         "multisort" : "_multisort"
4754     };
4755
4756     if(config && config.data){
4757         this.inlineData = config.data;
4758         delete config.data;
4759     }
4760
4761     Roo.apply(this, config);
4762     
4763     if(this.reader){ // reader passed
4764         this.reader = Roo.factory(this.reader, Roo.data);
4765         this.reader.xmodule = this.xmodule || false;
4766         if(!this.recordType){
4767             this.recordType = this.reader.recordType;
4768         }
4769         if(this.reader.onMetaChange){
4770             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4771         }
4772     }
4773
4774     if(this.recordType){
4775         this.fields = this.recordType.prototype.fields;
4776     }
4777     this.modified = [];
4778
4779     this.addEvents({
4780         /**
4781          * @event datachanged
4782          * Fires when the data cache has changed, and a widget which is using this Store
4783          * as a Record cache should refresh its view.
4784          * @param {Store} this
4785          */
4786         datachanged : true,
4787         /**
4788          * @event metachange
4789          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4790          * @param {Store} this
4791          * @param {Object} meta The JSON metadata
4792          */
4793         metachange : true,
4794         /**
4795          * @event add
4796          * Fires when Records have been added to the Store
4797          * @param {Store} this
4798          * @param {Roo.data.Record[]} records The array of Records added
4799          * @param {Number} index The index at which the record(s) were added
4800          */
4801         add : true,
4802         /**
4803          * @event remove
4804          * Fires when a Record has been removed from the Store
4805          * @param {Store} this
4806          * @param {Roo.data.Record} record The Record that was removed
4807          * @param {Number} index The index at which the record was removed
4808          */
4809         remove : true,
4810         /**
4811          * @event update
4812          * Fires when a Record has been updated
4813          * @param {Store} this
4814          * @param {Roo.data.Record} record The Record that was updated
4815          * @param {String} operation The update operation being performed.  Value may be one of:
4816          * <pre><code>
4817  Roo.data.Record.EDIT
4818  Roo.data.Record.REJECT
4819  Roo.data.Record.COMMIT
4820          * </code></pre>
4821          */
4822         update : true,
4823         /**
4824          * @event clear
4825          * Fires when the data cache has been cleared.
4826          * @param {Store} this
4827          */
4828         clear : true,
4829         /**
4830          * @event beforeload
4831          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4832          * the load action will be canceled.
4833          * @param {Store} this
4834          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4835          */
4836         beforeload : true,
4837         /**
4838          * @event beforeloadadd
4839          * Fires after a new set of Records has been loaded.
4840          * @param {Store} this
4841          * @param {Roo.data.Record[]} records The Records that were loaded
4842          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4843          */
4844         beforeloadadd : true,
4845         /**
4846          * @event load
4847          * Fires after a new set of Records has been loaded, before they are added to the store.
4848          * @param {Store} this
4849          * @param {Roo.data.Record[]} records The Records that were loaded
4850          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4851          * @params {Object} return from reader
4852          */
4853         load : true,
4854         /**
4855          * @event loadexception
4856          * Fires if an exception occurs in the Proxy during loading.
4857          * Called with the signature of the Proxy's "loadexception" event.
4858          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4859          * 
4860          * @param {Proxy} 
4861          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4862          * @param {Object} load options 
4863          * @param {Object} jsonData from your request (normally this contains the Exception)
4864          */
4865         loadexception : true
4866     });
4867     
4868     if(this.proxy){
4869         this.proxy = Roo.factory(this.proxy, Roo.data);
4870         this.proxy.xmodule = this.xmodule || false;
4871         this.relayEvents(this.proxy,  ["loadexception"]);
4872     }
4873     this.sortToggle = {};
4874     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4875
4876     Roo.data.Store.superclass.constructor.call(this);
4877
4878     if(this.inlineData){
4879         this.loadData(this.inlineData);
4880         delete this.inlineData;
4881     }
4882 };
4883
4884 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4885      /**
4886     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4887     * without a remote query - used by combo/forms at present.
4888     */
4889     
4890     /**
4891     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4892     */
4893     /**
4894     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4895     */
4896     /**
4897     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4898     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4899     */
4900     /**
4901     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4902     * on any HTTP request
4903     */
4904     /**
4905     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4906     */
4907     /**
4908     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4909     */
4910     multiSort: false,
4911     /**
4912     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4913     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4914     */
4915     remoteSort : false,
4916
4917     /**
4918     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4919      * loaded or when a record is removed. (defaults to false).
4920     */
4921     pruneModifiedRecords : false,
4922
4923     // private
4924     lastOptions : null,
4925
4926     /**
4927      * Add Records to the Store and fires the add event.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     add : function(records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             records[i].join(this);
4934         }
4935         var index = this.data.length;
4936         this.data.addAll(records);
4937         this.fireEvent("add", this, records, index);
4938     },
4939
4940     /**
4941      * Remove a Record from the Store and fires the remove event.
4942      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4943      */
4944     remove : function(record){
4945         var index = this.data.indexOf(record);
4946         this.data.removeAt(index);
4947         if(this.pruneModifiedRecords){
4948             this.modified.remove(record);
4949         }
4950         this.fireEvent("remove", this, record, index);
4951     },
4952
4953     /**
4954      * Remove all Records from the Store and fires the clear event.
4955      */
4956     removeAll : function(){
4957         this.data.clear();
4958         if(this.pruneModifiedRecords){
4959             this.modified = [];
4960         }
4961         this.fireEvent("clear", this);
4962     },
4963
4964     /**
4965      * Inserts Records to the Store at the given index and fires the add event.
4966      * @param {Number} index The start index at which to insert the passed Records.
4967      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4968      */
4969     insert : function(index, records){
4970         records = [].concat(records);
4971         for(var i = 0, len = records.length; i < len; i++){
4972             this.data.insert(index, records[i]);
4973             records[i].join(this);
4974         }
4975         this.fireEvent("add", this, records, index);
4976     },
4977
4978     /**
4979      * Get the index within the cache of the passed Record.
4980      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4981      * @return {Number} The index of the passed Record. Returns -1 if not found.
4982      */
4983     indexOf : function(record){
4984         return this.data.indexOf(record);
4985     },
4986
4987     /**
4988      * Get the index within the cache of the Record with the passed id.
4989      * @param {String} id The id of the Record to find.
4990      * @return {Number} The index of the Record. Returns -1 if not found.
4991      */
4992     indexOfId : function(id){
4993         return this.data.indexOfKey(id);
4994     },
4995
4996     /**
4997      * Get the Record with the specified id.
4998      * @param {String} id The id of the Record to find.
4999      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5000      */
5001     getById : function(id){
5002         return this.data.key(id);
5003     },
5004
5005     /**
5006      * Get the Record at the specified index.
5007      * @param {Number} index The index of the Record to find.
5008      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5009      */
5010     getAt : function(index){
5011         return this.data.itemAt(index);
5012     },
5013
5014     /**
5015      * Returns a range of Records between specified indices.
5016      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5017      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5018      * @return {Roo.data.Record[]} An array of Records
5019      */
5020     getRange : function(start, end){
5021         return this.data.getRange(start, end);
5022     },
5023
5024     // private
5025     storeOptions : function(o){
5026         o = Roo.apply({}, o);
5027         delete o.callback;
5028         delete o.scope;
5029         this.lastOptions = o;
5030     },
5031
5032     /**
5033      * Loads the Record cache from the configured Proxy using the configured Reader.
5034      * <p>
5035      * If using remote paging, then the first load call must specify the <em>start</em>
5036      * and <em>limit</em> properties in the options.params property to establish the initial
5037      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5038      * <p>
5039      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5040      * and this call will return before the new data has been loaded. Perform any post-processing
5041      * in a callback function, or in a "load" event handler.</strong>
5042      * <p>
5043      * @param {Object} options An object containing properties which control loading options:<ul>
5044      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5045      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5046      * passed the following arguments:<ul>
5047      * <li>r : Roo.data.Record[]</li>
5048      * <li>options: Options object from the load call</li>
5049      * <li>success: Boolean success indicator</li></ul></li>
5050      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5051      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5052      * </ul>
5053      */
5054     load : function(options){
5055         options = options || {};
5056         if(this.fireEvent("beforeload", this, options) !== false){
5057             this.storeOptions(options);
5058             var p = Roo.apply(options.params || {}, this.baseParams);
5059             // if meta was not loaded from remote source.. try requesting it.
5060             if (!this.reader.metaFromRemote) {
5061                 p._requestMeta = 1;
5062             }
5063             if(this.sortInfo && this.remoteSort){
5064                 var pn = this.paramNames;
5065                 p[pn["sort"]] = this.sortInfo.field;
5066                 p[pn["dir"]] = this.sortInfo.direction;
5067             }
5068             if (this.multiSort) {
5069                 var pn = this.paramNames;
5070                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5071             }
5072             
5073             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5074         }
5075     },
5076
5077     /**
5078      * Reloads the Record cache from the configured Proxy using the configured Reader and
5079      * the options from the last load operation performed.
5080      * @param {Object} options (optional) An object containing properties which may override the options
5081      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5082      * the most recently used options are reused).
5083      */
5084     reload : function(options){
5085         this.load(Roo.applyIf(options||{}, this.lastOptions));
5086     },
5087
5088     // private
5089     // Called as a callback by the Reader during a load operation.
5090     loadRecords : function(o, options, success){
5091         if(!o || success === false){
5092             if(success !== false){
5093                 this.fireEvent("load", this, [], options, o);
5094             }
5095             if(options.callback){
5096                 options.callback.call(options.scope || this, [], options, false);
5097             }
5098             return;
5099         }
5100         // if data returned failure - throw an exception.
5101         if (o.success === false) {
5102             // show a message if no listener is registered.
5103             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5104                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5105             }
5106             // loadmask wil be hooked into this..
5107             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5108             return;
5109         }
5110         var r = o.records, t = o.totalRecords || r.length;
5111         
5112         this.fireEvent("beforeloadadd", this, r, options, o);
5113         
5114         if(!options || options.add !== true){
5115             if(this.pruneModifiedRecords){
5116                 this.modified = [];
5117             }
5118             for(var i = 0, len = r.length; i < len; i++){
5119                 r[i].join(this);
5120             }
5121             if(this.snapshot){
5122                 this.data = this.snapshot;
5123                 delete this.snapshot;
5124             }
5125             this.data.clear();
5126             this.data.addAll(r);
5127             this.totalLength = t;
5128             this.applySort();
5129             this.fireEvent("datachanged", this);
5130         }else{
5131             this.totalLength = Math.max(t, this.data.length+r.length);
5132             this.add(r);
5133         }
5134         this.fireEvent("load", this, r, options, o);
5135         if(options.callback){
5136             options.callback.call(options.scope || this, r, options, true);
5137         }
5138     },
5139
5140
5141     /**
5142      * Loads data from a passed data block. A Reader which understands the format of the data
5143      * must have been configured in the constructor.
5144      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5145      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5146      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5147      */
5148     loadData : function(o, append){
5149         var r = this.reader.readRecords(o);
5150         this.loadRecords(r, {add: append}, true);
5151     },
5152
5153     /**
5154      * Gets the number of cached records.
5155      * <p>
5156      * <em>If using paging, this may not be the total size of the dataset. If the data object
5157      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5158      * the data set size</em>
5159      */
5160     getCount : function(){
5161         return this.data.length || 0;
5162     },
5163
5164     /**
5165      * Gets the total number of records in the dataset as returned by the server.
5166      * <p>
5167      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5168      * the dataset size</em>
5169      */
5170     getTotalCount : function(){
5171         return this.totalLength || 0;
5172     },
5173
5174     /**
5175      * Returns the sort state of the Store as an object with two properties:
5176      * <pre><code>
5177  field {String} The name of the field by which the Records are sorted
5178  direction {String} The sort order, "ASC" or "DESC"
5179      * </code></pre>
5180      */
5181     getSortState : function(){
5182         return this.sortInfo;
5183     },
5184
5185     // private
5186     applySort : function(){
5187         if(this.sortInfo && !this.remoteSort){
5188             var s = this.sortInfo, f = s.field;
5189             var st = this.fields.get(f).sortType;
5190             var fn = function(r1, r2){
5191                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5192                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5193             };
5194             this.data.sort(s.direction, fn);
5195             if(this.snapshot && this.snapshot != this.data){
5196                 this.snapshot.sort(s.direction, fn);
5197             }
5198         }
5199     },
5200
5201     /**
5202      * Sets the default sort column and order to be used by the next load operation.
5203      * @param {String} fieldName The name of the field to sort by.
5204      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5205      */
5206     setDefaultSort : function(field, dir){
5207         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5208     },
5209
5210     /**
5211      * Sort the Records.
5212      * If remote sorting is used, the sort is performed on the server, and the cache is
5213      * reloaded. If local sorting is used, the cache is sorted internally.
5214      * @param {String} fieldName The name of the field to sort by.
5215      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5216      */
5217     sort : function(fieldName, dir){
5218         var f = this.fields.get(fieldName);
5219         if(!dir){
5220             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5221             
5222             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5223                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5224             }else{
5225                 dir = f.sortDir;
5226             }
5227         }
5228         this.sortToggle[f.name] = dir;
5229         this.sortInfo = {field: f.name, direction: dir};
5230         if(!this.remoteSort){
5231             this.applySort();
5232             this.fireEvent("datachanged", this);
5233         }else{
5234             this.load(this.lastOptions);
5235         }
5236     },
5237
5238     /**
5239      * Calls the specified function for each of the Records in the cache.
5240      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5241      * Returning <em>false</em> aborts and exits the iteration.
5242      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5243      */
5244     each : function(fn, scope){
5245         this.data.each(fn, scope);
5246     },
5247
5248     /**
5249      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5250      * (e.g., during paging).
5251      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5252      */
5253     getModifiedRecords : function(){
5254         return this.modified;
5255     },
5256
5257     // private
5258     createFilterFn : function(property, value, anyMatch){
5259         if(!value.exec){ // not a regex
5260             value = String(value);
5261             if(value.length == 0){
5262                 return false;
5263             }
5264             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5265         }
5266         return function(r){
5267             return value.test(r.data[property]);
5268         };
5269     },
5270
5271     /**
5272      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5273      * @param {String} property A field on your records
5274      * @param {Number} start The record index to start at (defaults to 0)
5275      * @param {Number} end The last record index to include (defaults to length - 1)
5276      * @return {Number} The sum
5277      */
5278     sum : function(property, start, end){
5279         var rs = this.data.items, v = 0;
5280         start = start || 0;
5281         end = (end || end === 0) ? end : rs.length-1;
5282
5283         for(var i = start; i <= end; i++){
5284             v += (rs[i].data[property] || 0);
5285         }
5286         return v;
5287     },
5288
5289     /**
5290      * Filter the records by a specified property.
5291      * @param {String} field A field on your records
5292      * @param {String/RegExp} value Either a string that the field
5293      * should start with or a RegExp to test against the field
5294      * @param {Boolean} anyMatch True to match any part not just the beginning
5295      */
5296     filter : function(property, value, anyMatch){
5297         var fn = this.createFilterFn(property, value, anyMatch);
5298         return fn ? this.filterBy(fn) : this.clearFilter();
5299     },
5300
5301     /**
5302      * Filter by a function. The specified function will be called with each
5303      * record in this data source. If the function returns true the record is included,
5304      * otherwise it is filtered.
5305      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5306      * @param {Object} scope (optional) The scope of the function (defaults to this)
5307      */
5308     filterBy : function(fn, scope){
5309         this.snapshot = this.snapshot || this.data;
5310         this.data = this.queryBy(fn, scope||this);
5311         this.fireEvent("datachanged", this);
5312     },
5313
5314     /**
5315      * Query the records by a specified property.
5316      * @param {String} field A field on your records
5317      * @param {String/RegExp} value Either a string that the field
5318      * should start with or a RegExp to test against the field
5319      * @param {Boolean} anyMatch True to match any part not just the beginning
5320      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5321      */
5322     query : function(property, value, anyMatch){
5323         var fn = this.createFilterFn(property, value, anyMatch);
5324         return fn ? this.queryBy(fn) : this.data.clone();
5325     },
5326
5327     /**
5328      * Query by a function. The specified function will be called with each
5329      * record in this data source. If the function returns true the record is included
5330      * in the results.
5331      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5332      * @param {Object} scope (optional) The scope of the function (defaults to this)
5333       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5334      **/
5335     queryBy : function(fn, scope){
5336         var data = this.snapshot || this.data;
5337         return data.filterBy(fn, scope||this);
5338     },
5339
5340     /**
5341      * Collects unique values for a particular dataIndex from this store.
5342      * @param {String} dataIndex The property to collect
5343      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5344      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5345      * @return {Array} An array of the unique values
5346      **/
5347     collect : function(dataIndex, allowNull, bypassFilter){
5348         var d = (bypassFilter === true && this.snapshot) ?
5349                 this.snapshot.items : this.data.items;
5350         var v, sv, r = [], l = {};
5351         for(var i = 0, len = d.length; i < len; i++){
5352             v = d[i].data[dataIndex];
5353             sv = String(v);
5354             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5355                 l[sv] = true;
5356                 r[r.length] = v;
5357             }
5358         }
5359         return r;
5360     },
5361
5362     /**
5363      * Revert to a view of the Record cache with no filtering applied.
5364      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5365      */
5366     clearFilter : function(suppressEvent){
5367         if(this.snapshot && this.snapshot != this.data){
5368             this.data = this.snapshot;
5369             delete this.snapshot;
5370             if(suppressEvent !== true){
5371                 this.fireEvent("datachanged", this);
5372             }
5373         }
5374     },
5375
5376     // private
5377     afterEdit : function(record){
5378         if(this.modified.indexOf(record) == -1){
5379             this.modified.push(record);
5380         }
5381         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5382     },
5383     
5384     // private
5385     afterReject : function(record){
5386         this.modified.remove(record);
5387         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5388     },
5389
5390     // private
5391     afterCommit : function(record){
5392         this.modified.remove(record);
5393         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5394     },
5395
5396     /**
5397      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5398      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5399      */
5400     commitChanges : function(){
5401         var m = this.modified.slice(0);
5402         this.modified = [];
5403         for(var i = 0, len = m.length; i < len; i++){
5404             m[i].commit();
5405         }
5406     },
5407
5408     /**
5409      * Cancel outstanding changes on all changed records.
5410      */
5411     rejectChanges : function(){
5412         var m = this.modified.slice(0);
5413         this.modified = [];
5414         for(var i = 0, len = m.length; i < len; i++){
5415             m[i].reject();
5416         }
5417     },
5418
5419     onMetaChange : function(meta, rtype, o){
5420         this.recordType = rtype;
5421         this.fields = rtype.prototype.fields;
5422         delete this.snapshot;
5423         this.sortInfo = meta.sortInfo || this.sortInfo;
5424         this.modified = [];
5425         this.fireEvent('metachange', this, this.reader.meta);
5426     },
5427     
5428     moveIndex : function(data, type)
5429     {
5430         var index = this.indexOf(data);
5431         
5432         var newIndex = index + type;
5433         
5434         this.remove(data);
5435         
5436         this.insert(newIndex, data);
5437         
5438     }
5439 });/*
5440  * Based on:
5441  * Ext JS Library 1.1.1
5442  * Copyright(c) 2006-2007, Ext JS, LLC.
5443  *
5444  * Originally Released Under LGPL - original licence link has changed is not relivant.
5445  *
5446  * Fork - LGPL
5447  * <script type="text/javascript">
5448  */
5449
5450 /**
5451  * @class Roo.data.SimpleStore
5452  * @extends Roo.data.Store
5453  * Small helper class to make creating Stores from Array data easier.
5454  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5455  * @cfg {Array} fields An array of field definition objects, or field name strings.
5456  * @cfg {Array} data The multi-dimensional array of data
5457  * @constructor
5458  * @param {Object} config
5459  */
5460 Roo.data.SimpleStore = function(config){
5461     Roo.data.SimpleStore.superclass.constructor.call(this, {
5462         isLocal : true,
5463         reader: new Roo.data.ArrayReader({
5464                 id: config.id
5465             },
5466             Roo.data.Record.create(config.fields)
5467         ),
5468         proxy : new Roo.data.MemoryProxy(config.data)
5469     });
5470     this.load();
5471 };
5472 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483 /**
5484 /**
5485  * @extends Roo.data.Store
5486  * @class Roo.data.JsonStore
5487  * Small helper class to make creating Stores for JSON data easier. <br/>
5488 <pre><code>
5489 var store = new Roo.data.JsonStore({
5490     url: 'get-images.php',
5491     root: 'images',
5492     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5493 });
5494 </code></pre>
5495  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5496  * JsonReader and HttpProxy (unless inline data is provided).</b>
5497  * @cfg {Array} fields An array of field definition objects, or field name strings.
5498  * @constructor
5499  * @param {Object} config
5500  */
5501 Roo.data.JsonStore = function(c){
5502     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5503         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5504         reader: new Roo.data.JsonReader(c, c.fields)
5505     }));
5506 };
5507 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5508  * Based on:
5509  * Ext JS Library 1.1.1
5510  * Copyright(c) 2006-2007, Ext JS, LLC.
5511  *
5512  * Originally Released Under LGPL - original licence link has changed is not relivant.
5513  *
5514  * Fork - LGPL
5515  * <script type="text/javascript">
5516  */
5517
5518  
5519 Roo.data.Field = function(config){
5520     if(typeof config == "string"){
5521         config = {name: config};
5522     }
5523     Roo.apply(this, config);
5524     
5525     if(!this.type){
5526         this.type = "auto";
5527     }
5528     
5529     var st = Roo.data.SortTypes;
5530     // named sortTypes are supported, here we look them up
5531     if(typeof this.sortType == "string"){
5532         this.sortType = st[this.sortType];
5533     }
5534     
5535     // set default sortType for strings and dates
5536     if(!this.sortType){
5537         switch(this.type){
5538             case "string":
5539                 this.sortType = st.asUCString;
5540                 break;
5541             case "date":
5542                 this.sortType = st.asDate;
5543                 break;
5544             default:
5545                 this.sortType = st.none;
5546         }
5547     }
5548
5549     // define once
5550     var stripRe = /[\$,%]/g;
5551
5552     // prebuilt conversion function for this field, instead of
5553     // switching every time we're reading a value
5554     if(!this.convert){
5555         var cv, dateFormat = this.dateFormat;
5556         switch(this.type){
5557             case "":
5558             case "auto":
5559             case undefined:
5560                 cv = function(v){ return v; };
5561                 break;
5562             case "string":
5563                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5564                 break;
5565             case "int":
5566                 cv = function(v){
5567                     return v !== undefined && v !== null && v !== '' ?
5568                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5569                     };
5570                 break;
5571             case "float":
5572                 cv = function(v){
5573                     return v !== undefined && v !== null && v !== '' ?
5574                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5575                     };
5576                 break;
5577             case "bool":
5578             case "boolean":
5579                 cv = function(v){ return v === true || v === "true" || v == 1; };
5580                 break;
5581             case "date":
5582                 cv = function(v){
5583                     if(!v){
5584                         return '';
5585                     }
5586                     if(v instanceof Date){
5587                         return v;
5588                     }
5589                     if(dateFormat){
5590                         if(dateFormat == "timestamp"){
5591                             return new Date(v*1000);
5592                         }
5593                         return Date.parseDate(v, dateFormat);
5594                     }
5595                     var parsed = Date.parse(v);
5596                     return parsed ? new Date(parsed) : null;
5597                 };
5598              break;
5599             
5600         }
5601         this.convert = cv;
5602     }
5603 };
5604
5605 Roo.data.Field.prototype = {
5606     dateFormat: null,
5607     defaultValue: "",
5608     mapping: null,
5609     sortType : null,
5610     sortDir : "ASC"
5611 };/*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621  
5622 // Base class for reading structured data from a data source.  This class is intended to be
5623 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5624
5625 /**
5626  * @class Roo.data.DataReader
5627  * Base class for reading structured data from a data source.  This class is intended to be
5628  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5629  */
5630
5631 Roo.data.DataReader = function(meta, recordType){
5632     
5633     this.meta = meta;
5634     
5635     this.recordType = recordType instanceof Array ? 
5636         Roo.data.Record.create(recordType) : recordType;
5637 };
5638
5639 Roo.data.DataReader.prototype = {
5640      /**
5641      * Create an empty record
5642      * @param {Object} data (optional) - overlay some values
5643      * @return {Roo.data.Record} record created.
5644      */
5645     newRow :  function(d) {
5646         var da =  {};
5647         this.recordType.prototype.fields.each(function(c) {
5648             switch( c.type) {
5649                 case 'int' : da[c.name] = 0; break;
5650                 case 'date' : da[c.name] = new Date(); break;
5651                 case 'float' : da[c.name] = 0.0; break;
5652                 case 'boolean' : da[c.name] = false; break;
5653                 default : da[c.name] = ""; break;
5654             }
5655             
5656         });
5657         return new this.recordType(Roo.apply(da, d));
5658     }
5659     
5660 };/*
5661  * Based on:
5662  * Ext JS Library 1.1.1
5663  * Copyright(c) 2006-2007, Ext JS, LLC.
5664  *
5665  * Originally Released Under LGPL - original licence link has changed is not relivant.
5666  *
5667  * Fork - LGPL
5668  * <script type="text/javascript">
5669  */
5670
5671 /**
5672  * @class Roo.data.DataProxy
5673  * @extends Roo.data.Observable
5674  * This class is an abstract base class for implementations which provide retrieval of
5675  * unformatted data objects.<br>
5676  * <p>
5677  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5678  * (of the appropriate type which knows how to parse the data object) to provide a block of
5679  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5680  * <p>
5681  * Custom implementations must implement the load method as described in
5682  * {@link Roo.data.HttpProxy#load}.
5683  */
5684 Roo.data.DataProxy = function(){
5685     this.addEvents({
5686         /**
5687          * @event beforeload
5688          * Fires before a network request is made to retrieve a data object.
5689          * @param {Object} This DataProxy object.
5690          * @param {Object} params The params parameter to the load function.
5691          */
5692         beforeload : true,
5693         /**
5694          * @event load
5695          * Fires before the load method's callback is called.
5696          * @param {Object} This DataProxy object.
5697          * @param {Object} o The data object.
5698          * @param {Object} arg The callback argument object passed to the load function.
5699          */
5700         load : true,
5701         /**
5702          * @event loadexception
5703          * Fires if an Exception occurs during data retrieval.
5704          * @param {Object} This DataProxy object.
5705          * @param {Object} o The data object.
5706          * @param {Object} arg The callback argument object passed to the load function.
5707          * @param {Object} e The Exception.
5708          */
5709         loadexception : true
5710     });
5711     Roo.data.DataProxy.superclass.constructor.call(this);
5712 };
5713
5714 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5715
5716     /**
5717      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5718      */
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.MemoryProxy
5731  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5732  * to the Reader when its load method is called.
5733  * @constructor
5734  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5735  */
5736 Roo.data.MemoryProxy = function(data){
5737     if (data.data) {
5738         data = data.data;
5739     }
5740     Roo.data.MemoryProxy.superclass.constructor.call(this);
5741     this.data = data;
5742 };
5743
5744 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5745     /**
5746      * Load data from the requested source (in this case an in-memory
5747      * data object passed to the constructor), read the data object into
5748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5749      * process that block using the passed callback.
5750      * @param {Object} params This parameter is not used by the MemoryProxy class.
5751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5752      * object into a block of Roo.data.Records.
5753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5754      * The function must be passed <ul>
5755      * <li>The Record block object</li>
5756      * <li>The "arg" argument from the load function</li>
5757      * <li>A boolean success indicator</li>
5758      * </ul>
5759      * @param {Object} scope The scope in which to call the callback
5760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5761      */
5762     load : function(params, reader, callback, scope, arg){
5763         params = params || {};
5764         var result;
5765         try {
5766             result = reader.readRecords(this.data);
5767         }catch(e){
5768             this.fireEvent("loadexception", this, arg, null, e);
5769             callback.call(scope, null, arg, false);
5770             return;
5771         }
5772         callback.call(scope, result, arg, true);
5773     },
5774     
5775     // private
5776     update : function(params, records){
5777         
5778     }
5779 });/*
5780  * Based on:
5781  * Ext JS Library 1.1.1
5782  * Copyright(c) 2006-2007, Ext JS, LLC.
5783  *
5784  * Originally Released Under LGPL - original licence link has changed is not relivant.
5785  *
5786  * Fork - LGPL
5787  * <script type="text/javascript">
5788  */
5789 /**
5790  * @class Roo.data.HttpProxy
5791  * @extends Roo.data.DataProxy
5792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5793  * configured to reference a certain URL.<br><br>
5794  * <p>
5795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5796  * from which the running page was served.<br><br>
5797  * <p>
5798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5799  * <p>
5800  * Be aware that to enable the browser to parse an XML document, the server must set
5801  * the Content-Type header in the HTTP response to "text/xml".
5802  * @constructor
5803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5805  * will be used to make the request.
5806  */
5807 Roo.data.HttpProxy = function(conn){
5808     Roo.data.HttpProxy.superclass.constructor.call(this);
5809     // is conn a conn config or a real conn?
5810     this.conn = conn;
5811     this.useAjax = !conn || !conn.events;
5812   
5813 };
5814
5815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5816     // thse are take from connection...
5817     
5818     /**
5819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5823      * extra parameters to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5827      *  to each request made by this object. (defaults to undefined)
5828      */
5829     /**
5830      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5834      */
5835      /**
5836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5837      * @type Boolean
5838      */
5839   
5840
5841     /**
5842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5843      * @type Boolean
5844      */
5845     /**
5846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5848      * a finer-grained basis than the DataProxy events.
5849      */
5850     getConnection : function(){
5851         return this.useAjax ? Roo.Ajax : this.conn;
5852     },
5853
5854     /**
5855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5857      * process that block using the passed callback.
5858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5859      * for the request to the remote server.
5860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5861      * object into a block of Roo.data.Records.
5862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5863      * The function must be passed <ul>
5864      * <li>The Record block object</li>
5865      * <li>The "arg" argument from the load function</li>
5866      * <li>A boolean success indicator</li>
5867      * </ul>
5868      * @param {Object} scope The scope in which to call the callback
5869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5870      */
5871     load : function(params, reader, callback, scope, arg){
5872         if(this.fireEvent("beforeload", this, params) !== false){
5873             var  o = {
5874                 params : params || {},
5875                 request: {
5876                     callback : callback,
5877                     scope : scope,
5878                     arg : arg
5879                 },
5880                 reader: reader,
5881                 callback : this.loadResponse,
5882                 scope: this
5883             };
5884             if(this.useAjax){
5885                 Roo.applyIf(o, this.conn);
5886                 if(this.activeRequest){
5887                     Roo.Ajax.abort(this.activeRequest);
5888                 }
5889                 this.activeRequest = Roo.Ajax.request(o);
5890             }else{
5891                 this.conn.request(o);
5892             }
5893         }else{
5894             callback.call(scope||this, null, arg, false);
5895         }
5896     },
5897
5898     // private
5899     loadResponse : function(o, success, response){
5900         delete this.activeRequest;
5901         if(!success){
5902             this.fireEvent("loadexception", this, o, response);
5903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5904             return;
5905         }
5906         var result;
5907         try {
5908             result = o.reader.read(response);
5909         }catch(e){
5910             this.fireEvent("loadexception", this, o, response, e);
5911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5912             return;
5913         }
5914         
5915         this.fireEvent("load", this, o, o.request.arg);
5916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5917     },
5918
5919     // private
5920     update : function(dataSet){
5921
5922     },
5923
5924     // private
5925     updateResponse : function(dataSet){
5926
5927     }
5928 });/*
5929  * Based on:
5930  * Ext JS Library 1.1.1
5931  * Copyright(c) 2006-2007, Ext JS, LLC.
5932  *
5933  * Originally Released Under LGPL - original licence link has changed is not relivant.
5934  *
5935  * Fork - LGPL
5936  * <script type="text/javascript">
5937  */
5938
5939 /**
5940  * @class Roo.data.ScriptTagProxy
5941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5942  * other than the originating domain of the running page.<br><br>
5943  * <p>
5944  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5946  * <p>
5947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5948  * source code that is used as the source inside a &lt;script> tag.<br><br>
5949  * <p>
5950  * In order for the browser to process the returned data, the server must wrap the data object
5951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5953  * depending on whether the callback name was passed:
5954  * <p>
5955  * <pre><code>
5956 boolean scriptTag = false;
5957 String cb = request.getParameter("callback");
5958 if (cb != null) {
5959     scriptTag = true;
5960     response.setContentType("text/javascript");
5961 } else {
5962     response.setContentType("application/x-json");
5963 }
5964 Writer out = response.getWriter();
5965 if (scriptTag) {
5966     out.write(cb + "(");
5967 }
5968 out.print(dataBlock.toJsonString());
5969 if (scriptTag) {
5970     out.write(");");
5971 }
5972 </pre></code>
5973  *
5974  * @constructor
5975  * @param {Object} config A configuration object.
5976  */
5977 Roo.data.ScriptTagProxy = function(config){
5978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5979     Roo.apply(this, config);
5980     this.head = document.getElementsByTagName("head")[0];
5981 };
5982
5983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5984
5985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5986     /**
5987      * @cfg {String} url The URL from which to request the data object.
5988      */
5989     /**
5990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5991      */
5992     timeout : 30000,
5993     /**
5994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5995      * the server the name of the callback function set up by the load call to process the returned data object.
5996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5997      * javascript output which calls this named function passing the data object as its only parameter.
5998      */
5999     callbackParam : "callback",
6000     /**
6001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6002      * name to the request.
6003      */
6004     nocache : true,
6005
6006     /**
6007      * Load data from the configured URL, read the data object into
6008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6009      * process that block using the passed callback.
6010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6011      * for the request to the remote server.
6012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6013      * object into a block of Roo.data.Records.
6014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6015      * The function must be passed <ul>
6016      * <li>The Record block object</li>
6017      * <li>The "arg" argument from the load function</li>
6018      * <li>A boolean success indicator</li>
6019      * </ul>
6020      * @param {Object} scope The scope in which to call the callback
6021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6022      */
6023     load : function(params, reader, callback, scope, arg){
6024         if(this.fireEvent("beforeload", this, params) !== false){
6025
6026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6027
6028             var url = this.url;
6029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6030             if(this.nocache){
6031                 url += "&_dc=" + (new Date().getTime());
6032             }
6033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6034             var trans = {
6035                 id : transId,
6036                 cb : "stcCallback"+transId,
6037                 scriptId : "stcScript"+transId,
6038                 params : params,
6039                 arg : arg,
6040                 url : url,
6041                 callback : callback,
6042                 scope : scope,
6043                 reader : reader
6044             };
6045             var conn = this;
6046
6047             window[trans.cb] = function(o){
6048                 conn.handleResponse(o, trans);
6049             };
6050
6051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6052
6053             if(this.autoAbort !== false){
6054                 this.abort();
6055             }
6056
6057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6058
6059             var script = document.createElement("script");
6060             script.setAttribute("src", url);
6061             script.setAttribute("type", "text/javascript");
6062             script.setAttribute("id", trans.scriptId);
6063             this.head.appendChild(script);
6064
6065             this.trans = trans;
6066         }else{
6067             callback.call(scope||this, null, arg, false);
6068         }
6069     },
6070
6071     // private
6072     isLoading : function(){
6073         return this.trans ? true : false;
6074     },
6075
6076     /**
6077      * Abort the current server request.
6078      */
6079     abort : function(){
6080         if(this.isLoading()){
6081             this.destroyTrans(this.trans);
6082         }
6083     },
6084
6085     // private
6086     destroyTrans : function(trans, isLoaded){
6087         this.head.removeChild(document.getElementById(trans.scriptId));
6088         clearTimeout(trans.timeoutId);
6089         if(isLoaded){
6090             window[trans.cb] = undefined;
6091             try{
6092                 delete window[trans.cb];
6093             }catch(e){}
6094         }else{
6095             // if hasn't been loaded, wait for load to remove it to prevent script error
6096             window[trans.cb] = function(){
6097                 window[trans.cb] = undefined;
6098                 try{
6099                     delete window[trans.cb];
6100                 }catch(e){}
6101             };
6102         }
6103     },
6104
6105     // private
6106     handleResponse : function(o, trans){
6107         this.trans = false;
6108         this.destroyTrans(trans, true);
6109         var result;
6110         try {
6111             result = trans.reader.readRecords(o);
6112         }catch(e){
6113             this.fireEvent("loadexception", this, o, trans.arg, e);
6114             trans.callback.call(trans.scope||window, null, trans.arg, false);
6115             return;
6116         }
6117         this.fireEvent("load", this, o, trans.arg);
6118         trans.callback.call(trans.scope||window, result, trans.arg, true);
6119     },
6120
6121     // private
6122     handleFailure : function(trans){
6123         this.trans = false;
6124         this.destroyTrans(trans, false);
6125         this.fireEvent("loadexception", this, null, trans.arg);
6126         trans.callback.call(trans.scope||window, null, trans.arg, false);
6127     }
6128 });/*
6129  * Based on:
6130  * Ext JS Library 1.1.1
6131  * Copyright(c) 2006-2007, Ext JS, LLC.
6132  *
6133  * Originally Released Under LGPL - original licence link has changed is not relivant.
6134  *
6135  * Fork - LGPL
6136  * <script type="text/javascript">
6137  */
6138
6139 /**
6140  * @class Roo.data.JsonReader
6141  * @extends Roo.data.DataReader
6142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6143  * based on mappings in a provided Roo.data.Record constructor.
6144  * 
6145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6146  * in the reply previously. 
6147  * 
6148  * <p>
6149  * Example code:
6150  * <pre><code>
6151 var RecordDef = Roo.data.Record.create([
6152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6154 ]);
6155 var myReader = new Roo.data.JsonReader({
6156     totalProperty: "results",    // The property which contains the total dataset size (optional)
6157     root: "rows",                // The property which contains an Array of row objects
6158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6159 }, RecordDef);
6160 </code></pre>
6161  * <p>
6162  * This would consume a JSON file like this:
6163  * <pre><code>
6164 { 'results': 2, 'rows': [
6165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6167 }
6168 </code></pre>
6169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6171  * paged from the remote server.
6172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6173  * @cfg {String} root name of the property which contains the Array of row objects.
6174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6175  * @cfg {Array} fields Array of field definition objects
6176  * @constructor
6177  * Create a new JsonReader
6178  * @param {Object} meta Metadata configuration options
6179  * @param {Object} recordType Either an Array of field definition objects,
6180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6181  */
6182 Roo.data.JsonReader = function(meta, recordType){
6183     
6184     meta = meta || {};
6185     // set some defaults:
6186     Roo.applyIf(meta, {
6187         totalProperty: 'total',
6188         successProperty : 'success',
6189         root : 'data',
6190         id : 'id'
6191     });
6192     
6193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6194 };
6195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6196     
6197     /**
6198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6199      * Used by Store query builder to append _requestMeta to params.
6200      * 
6201      */
6202     metaFromRemote : false,
6203     /**
6204      * This method is only used by a DataProxy which has retrieved data from a remote server.
6205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6206      * @return {Object} data A data block which is used by an Roo.data.Store object as
6207      * a cache of Roo.data.Records.
6208      */
6209     read : function(response){
6210         var json = response.responseText;
6211        
6212         var o = /* eval:var:o */ eval("("+json+")");
6213         if(!o) {
6214             throw {message: "JsonReader.read: Json object not found"};
6215         }
6216         
6217         if(o.metaData){
6218             
6219             delete this.ef;
6220             this.metaFromRemote = true;
6221             this.meta = o.metaData;
6222             this.recordType = Roo.data.Record.create(o.metaData.fields);
6223             this.onMetaChange(this.meta, this.recordType, o);
6224         }
6225         return this.readRecords(o);
6226     },
6227
6228     // private function a store will implement
6229     onMetaChange : function(meta, recordType, o){
6230
6231     },
6232
6233     /**
6234          * @ignore
6235          */
6236     simpleAccess: function(obj, subsc) {
6237         return obj[subsc];
6238     },
6239
6240         /**
6241          * @ignore
6242          */
6243     getJsonAccessor: function(){
6244         var re = /[\[\.]/;
6245         return function(expr) {
6246             try {
6247                 return(re.test(expr))
6248                     ? new Function("obj", "return obj." + expr)
6249                     : function(obj){
6250                         return obj[expr];
6251                     };
6252             } catch(e){}
6253             return Roo.emptyFn;
6254         };
6255     }(),
6256
6257     /**
6258      * Create a data block containing Roo.data.Records from an XML document.
6259      * @param {Object} o An object which contains an Array of row objects in the property specified
6260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6261      * which contains the total size of the dataset.
6262      * @return {Object} data A data block which is used by an Roo.data.Store object as
6263      * a cache of Roo.data.Records.
6264      */
6265     readRecords : function(o){
6266         /**
6267          * After any data loads, the raw JSON data is available for further custom processing.
6268          * @type Object
6269          */
6270         this.o = o;
6271         var s = this.meta, Record = this.recordType,
6272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6273
6274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6275         if (!this.ef) {
6276             if(s.totalProperty) {
6277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6278                 }
6279                 if(s.successProperty) {
6280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6281                 }
6282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6283                 if (s.id) {
6284                         var g = this.getJsonAccessor(s.id);
6285                         this.getId = function(rec) {
6286                                 var r = g(rec);  
6287                                 return (r === undefined || r === "") ? null : r;
6288                         };
6289                 } else {
6290                         this.getId = function(){return null;};
6291                 }
6292             this.ef = [];
6293             for(var jj = 0; jj < fl; jj++){
6294                 f = fi[jj];
6295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6296                 this.ef[jj] = this.getJsonAccessor(map);
6297             }
6298         }
6299
6300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6301         if(s.totalProperty){
6302             var vt = parseInt(this.getTotal(o), 10);
6303             if(!isNaN(vt)){
6304                 totalRecords = vt;
6305             }
6306         }
6307         if(s.successProperty){
6308             var vs = this.getSuccess(o);
6309             if(vs === false || vs === 'false'){
6310                 success = false;
6311             }
6312         }
6313         var records = [];
6314         for(var i = 0; i < c; i++){
6315                 var n = root[i];
6316             var values = {};
6317             var id = this.getId(n);
6318             for(var j = 0; j < fl; j++){
6319                 f = fi[j];
6320             var v = this.ef[j](n);
6321             if (!f.convert) {
6322                 Roo.log('missing convert for ' + f.name);
6323                 Roo.log(f);
6324                 continue;
6325             }
6326             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6327             }
6328             var record = new Record(values, id);
6329             record.json = n;
6330             records[i] = record;
6331         }
6332         return {
6333             raw : o,
6334             success : success,
6335             records : records,
6336             totalRecords : totalRecords
6337         };
6338     }
6339 });/*
6340  * Based on:
6341  * Ext JS Library 1.1.1
6342  * Copyright(c) 2006-2007, Ext JS, LLC.
6343  *
6344  * Originally Released Under LGPL - original licence link has changed is not relivant.
6345  *
6346  * Fork - LGPL
6347  * <script type="text/javascript">
6348  */
6349
6350 /**
6351  * @class Roo.data.XmlReader
6352  * @extends Roo.data.DataReader
6353  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6354  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6355  * <p>
6356  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6357  * header in the HTTP response must be set to "text/xml".</em>
6358  * <p>
6359  * Example code:
6360  * <pre><code>
6361 var RecordDef = Roo.data.Record.create([
6362    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6363    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6364 ]);
6365 var myReader = new Roo.data.XmlReader({
6366    totalRecords: "results", // The element which contains the total dataset size (optional)
6367    record: "row",           // The repeated element which contains row information
6368    id: "id"                 // The element within the row that provides an ID for the record (optional)
6369 }, RecordDef);
6370 </code></pre>
6371  * <p>
6372  * This would consume an XML file like this:
6373  * <pre><code>
6374 &lt;?xml?>
6375 &lt;dataset>
6376  &lt;results>2&lt;/results>
6377  &lt;row>
6378    &lt;id>1&lt;/id>
6379    &lt;name>Bill&lt;/name>
6380    &lt;occupation>Gardener&lt;/occupation>
6381  &lt;/row>
6382  &lt;row>
6383    &lt;id>2&lt;/id>
6384    &lt;name>Ben&lt;/name>
6385    &lt;occupation>Horticulturalist&lt;/occupation>
6386  &lt;/row>
6387 &lt;/dataset>
6388 </code></pre>
6389  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6390  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6391  * paged from the remote server.
6392  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6393  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6394  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6395  * a record identifier value.
6396  * @constructor
6397  * Create a new XmlReader
6398  * @param {Object} meta Metadata configuration options
6399  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6400  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6401  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6402  */
6403 Roo.data.XmlReader = function(meta, recordType){
6404     meta = meta || {};
6405     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6406 };
6407 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6408     /**
6409      * This method is only used by a DataProxy which has retrieved data from a remote server.
6410          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6411          * to contain a method called 'responseXML' that returns an XML document object.
6412      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6413      * a cache of Roo.data.Records.
6414      */
6415     read : function(response){
6416         var doc = response.responseXML;
6417         if(!doc) {
6418             throw {message: "XmlReader.read: XML Document not available"};
6419         }
6420         return this.readRecords(doc);
6421     },
6422
6423     /**
6424      * Create a data block containing Roo.data.Records from an XML document.
6425          * @param {Object} doc A parsed XML document.
6426      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6427      * a cache of Roo.data.Records.
6428      */
6429     readRecords : function(doc){
6430         /**
6431          * After any data loads/reads, the raw XML Document is available for further custom processing.
6432          * @type XMLDocument
6433          */
6434         this.xmlData = doc;
6435         var root = doc.documentElement || doc;
6436         var q = Roo.DomQuery;
6437         var recordType = this.recordType, fields = recordType.prototype.fields;
6438         var sid = this.meta.id;
6439         var totalRecords = 0, success = true;
6440         if(this.meta.totalRecords){
6441             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6442         }
6443         
6444         if(this.meta.success){
6445             var sv = q.selectValue(this.meta.success, root, true);
6446             success = sv !== false && sv !== 'false';
6447         }
6448         var records = [];
6449         var ns = q.select(this.meta.record, root);
6450         for(var i = 0, len = ns.length; i < len; i++) {
6451                 var n = ns[i];
6452                 var values = {};
6453                 var id = sid ? q.selectValue(sid, n) : undefined;
6454                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455                     var f = fields.items[j];
6456                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6457                     v = f.convert(v);
6458                     values[f.name] = v;
6459                 }
6460                 var record = new recordType(values, id);
6461                 record.node = n;
6462                 records[records.length] = record;
6463             }
6464
6465             return {
6466                 success : success,
6467                 records : records,
6468                 totalRecords : totalRecords || records.length
6469             };
6470     }
6471 });/*
6472  * Based on:
6473  * Ext JS Library 1.1.1
6474  * Copyright(c) 2006-2007, Ext JS, LLC.
6475  *
6476  * Originally Released Under LGPL - original licence link has changed is not relivant.
6477  *
6478  * Fork - LGPL
6479  * <script type="text/javascript">
6480  */
6481
6482 /**
6483  * @class Roo.data.ArrayReader
6484  * @extends Roo.data.DataReader
6485  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6486  * Each element of that Array represents a row of data fields. The
6487  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6488  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6489  * <p>
6490  * Example code:.
6491  * <pre><code>
6492 var RecordDef = Roo.data.Record.create([
6493     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6494     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6495 ]);
6496 var myReader = new Roo.data.ArrayReader({
6497     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6498 }, RecordDef);
6499 </code></pre>
6500  * <p>
6501  * This would consume an Array like this:
6502  * <pre><code>
6503 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6504   </code></pre>
6505  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6506  * @constructor
6507  * Create a new JsonReader
6508  * @param {Object} meta Metadata configuration options.
6509  * @param {Object} recordType Either an Array of field definition objects
6510  * as specified to {@link Roo.data.Record#create},
6511  * or an {@link Roo.data.Record} object
6512  * created using {@link Roo.data.Record#create}.
6513  */
6514 Roo.data.ArrayReader = function(meta, recordType){
6515     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6516 };
6517
6518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6519     /**
6520      * Create a data block containing Roo.data.Records from an XML document.
6521      * @param {Object} o An Array of row objects which represents the dataset.
6522      * @return {Object} data A data block which is used by an Roo.data.Store object as
6523      * a cache of Roo.data.Records.
6524      */
6525     readRecords : function(o){
6526         var sid = this.meta ? this.meta.id : null;
6527         var recordType = this.recordType, fields = recordType.prototype.fields;
6528         var records = [];
6529         var root = o;
6530             for(var i = 0; i < root.length; i++){
6531                     var n = root[i];
6532                 var values = {};
6533                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6534                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6535                 var f = fields.items[j];
6536                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6537                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6538                 v = f.convert(v);
6539                 values[f.name] = v;
6540             }
6541                 var record = new recordType(values, id);
6542                 record.json = n;
6543                 records[records.length] = record;
6544             }
6545             return {
6546                 records : records,
6547                 totalRecords : records.length
6548             };
6549     }
6550 });/*
6551  * Based on:
6552  * Ext JS Library 1.1.1
6553  * Copyright(c) 2006-2007, Ext JS, LLC.
6554  *
6555  * Originally Released Under LGPL - original licence link has changed is not relivant.
6556  *
6557  * Fork - LGPL
6558  * <script type="text/javascript">
6559  */
6560
6561
6562 /**
6563  * @class Roo.data.Tree
6564  * @extends Roo.util.Observable
6565  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6566  * in the tree have most standard DOM functionality.
6567  * @constructor
6568  * @param {Node} root (optional) The root node
6569  */
6570 Roo.data.Tree = function(root){
6571    this.nodeHash = {};
6572    /**
6573     * The root node for this tree
6574     * @type Node
6575     */
6576    this.root = null;
6577    if(root){
6578        this.setRootNode(root);
6579    }
6580    this.addEvents({
6581        /**
6582         * @event append
6583         * Fires when a new child node is appended to a node in this tree.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The newly appended node
6587         * @param {Number} index The index of the newly appended node
6588         */
6589        "append" : true,
6590        /**
6591         * @event remove
6592         * Fires when a child node is removed from a node in this tree.
6593         * @param {Tree} tree The owner tree
6594         * @param {Node} parent The parent node
6595         * @param {Node} node The child node removed
6596         */
6597        "remove" : true,
6598        /**
6599         * @event move
6600         * Fires when a node is moved to a new location in the tree
6601         * @param {Tree} tree The owner tree
6602         * @param {Node} node The node moved
6603         * @param {Node} oldParent The old parent of this node
6604         * @param {Node} newParent The new parent of this node
6605         * @param {Number} index The index it was moved to
6606         */
6607        "move" : true,
6608        /**
6609         * @event insert
6610         * Fires when a new child node is inserted in a node in this tree.
6611         * @param {Tree} tree The owner tree
6612         * @param {Node} parent The parent node
6613         * @param {Node} node The child node inserted
6614         * @param {Node} refNode The child node the node was inserted before
6615         */
6616        "insert" : true,
6617        /**
6618         * @event beforeappend
6619         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6620         * @param {Tree} tree The owner tree
6621         * @param {Node} parent The parent node
6622         * @param {Node} node The child node to be appended
6623         */
6624        "beforeappend" : true,
6625        /**
6626         * @event beforeremove
6627         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6628         * @param {Tree} tree The owner tree
6629         * @param {Node} parent The parent node
6630         * @param {Node} node The child node to be removed
6631         */
6632        "beforeremove" : true,
6633        /**
6634         * @event beforemove
6635         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6636         * @param {Tree} tree The owner tree
6637         * @param {Node} node The node being moved
6638         * @param {Node} oldParent The parent of the node
6639         * @param {Node} newParent The new parent the node is moving to
6640         * @param {Number} index The index it is being moved to
6641         */
6642        "beforemove" : true,
6643        /**
6644         * @event beforeinsert
6645         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} parent The parent node
6648         * @param {Node} node The child node to be inserted
6649         * @param {Node} refNode The child node the node is being inserted before
6650         */
6651        "beforeinsert" : true
6652    });
6653
6654     Roo.data.Tree.superclass.constructor.call(this);
6655 };
6656
6657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6658     pathSeparator: "/",
6659
6660     proxyNodeEvent : function(){
6661         return this.fireEvent.apply(this, arguments);
6662     },
6663
6664     /**
6665      * Returns the root node for this tree.
6666      * @return {Node}
6667      */
6668     getRootNode : function(){
6669         return this.root;
6670     },
6671
6672     /**
6673      * Sets the root node for this tree.
6674      * @param {Node} node
6675      * @return {Node}
6676      */
6677     setRootNode : function(node){
6678         this.root = node;
6679         node.ownerTree = this;
6680         node.isRoot = true;
6681         this.registerNode(node);
6682         return node;
6683     },
6684
6685     /**
6686      * Gets a node in this tree by its id.
6687      * @param {String} id
6688      * @return {Node}
6689      */
6690     getNodeById : function(id){
6691         return this.nodeHash[id];
6692     },
6693
6694     registerNode : function(node){
6695         this.nodeHash[node.id] = node;
6696     },
6697
6698     unregisterNode : function(node){
6699         delete this.nodeHash[node.id];
6700     },
6701
6702     toString : function(){
6703         return "[Tree"+(this.id?" "+this.id:"")+"]";
6704     }
6705 });
6706
6707 /**
6708  * @class Roo.data.Node
6709  * @extends Roo.util.Observable
6710  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6711  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6712  * @constructor
6713  * @param {Object} attributes The attributes/config for the node
6714  */
6715 Roo.data.Node = function(attributes){
6716     /**
6717      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6718      * @type {Object}
6719      */
6720     this.attributes = attributes || {};
6721     this.leaf = this.attributes.leaf;
6722     /**
6723      * The node id. @type String
6724      */
6725     this.id = this.attributes.id;
6726     if(!this.id){
6727         this.id = Roo.id(null, "ynode-");
6728         this.attributes.id = this.id;
6729     }
6730      
6731     
6732     /**
6733      * All child nodes of this node. @type Array
6734      */
6735     this.childNodes = [];
6736     if(!this.childNodes.indexOf){ // indexOf is a must
6737         this.childNodes.indexOf = function(o){
6738             for(var i = 0, len = this.length; i < len; i++){
6739                 if(this[i] == o) {
6740                     return i;
6741                 }
6742             }
6743             return -1;
6744         };
6745     }
6746     /**
6747      * The parent node for this node. @type Node
6748      */
6749     this.parentNode = null;
6750     /**
6751      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6752      */
6753     this.firstChild = null;
6754     /**
6755      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6756      */
6757     this.lastChild = null;
6758     /**
6759      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6760      */
6761     this.previousSibling = null;
6762     /**
6763      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6764      */
6765     this.nextSibling = null;
6766
6767     this.addEvents({
6768        /**
6769         * @event append
6770         * Fires when a new child node is appended
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The newly appended node
6774         * @param {Number} index The index of the newly appended node
6775         */
6776        "append" : true,
6777        /**
6778         * @event remove
6779         * Fires when a child node is removed
6780         * @param {Tree} tree The owner tree
6781         * @param {Node} this This node
6782         * @param {Node} node The removed node
6783         */
6784        "remove" : true,
6785        /**
6786         * @event move
6787         * Fires when this node is moved to a new location in the tree
6788         * @param {Tree} tree The owner tree
6789         * @param {Node} this This node
6790         * @param {Node} oldParent The old parent of this node
6791         * @param {Node} newParent The new parent of this node
6792         * @param {Number} index The index it was moved to
6793         */
6794        "move" : true,
6795        /**
6796         * @event insert
6797         * Fires when a new child node is inserted.
6798         * @param {Tree} tree The owner tree
6799         * @param {Node} this This node
6800         * @param {Node} node The child node inserted
6801         * @param {Node} refNode The child node the node was inserted before
6802         */
6803        "insert" : true,
6804        /**
6805         * @event beforeappend
6806         * Fires before a new child is appended, return false to cancel the append.
6807         * @param {Tree} tree The owner tree
6808         * @param {Node} this This node
6809         * @param {Node} node The child node to be appended
6810         */
6811        "beforeappend" : true,
6812        /**
6813         * @event beforeremove
6814         * Fires before a child is removed, return false to cancel the remove.
6815         * @param {Tree} tree The owner tree
6816         * @param {Node} this This node
6817         * @param {Node} node The child node to be removed
6818         */
6819        "beforeremove" : true,
6820        /**
6821         * @event beforemove
6822         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6823         * @param {Tree} tree The owner tree
6824         * @param {Node} this This node
6825         * @param {Node} oldParent The parent of this node
6826         * @param {Node} newParent The new parent this node is moving to
6827         * @param {Number} index The index it is being moved to
6828         */
6829        "beforemove" : true,
6830        /**
6831         * @event beforeinsert
6832         * Fires before a new child is inserted, return false to cancel the insert.
6833         * @param {Tree} tree The owner tree
6834         * @param {Node} this This node
6835         * @param {Node} node The child node to be inserted
6836         * @param {Node} refNode The child node the node is being inserted before
6837         */
6838        "beforeinsert" : true
6839    });
6840     this.listeners = this.attributes.listeners;
6841     Roo.data.Node.superclass.constructor.call(this);
6842 };
6843
6844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6845     fireEvent : function(evtName){
6846         // first do standard event for this node
6847         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6848             return false;
6849         }
6850         // then bubble it up to the tree if the event wasn't cancelled
6851         var ot = this.getOwnerTree();
6852         if(ot){
6853             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6854                 return false;
6855             }
6856         }
6857         return true;
6858     },
6859
6860     /**
6861      * Returns true if this node is a leaf
6862      * @return {Boolean}
6863      */
6864     isLeaf : function(){
6865         return this.leaf === true;
6866     },
6867
6868     // private
6869     setFirstChild : function(node){
6870         this.firstChild = node;
6871     },
6872
6873     //private
6874     setLastChild : function(node){
6875         this.lastChild = node;
6876     },
6877
6878
6879     /**
6880      * Returns true if this node is the last child of its parent
6881      * @return {Boolean}
6882      */
6883     isLast : function(){
6884        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6885     },
6886
6887     /**
6888      * Returns true if this node is the first child of its parent
6889      * @return {Boolean}
6890      */
6891     isFirst : function(){
6892        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6893     },
6894
6895     hasChildNodes : function(){
6896         return !this.isLeaf() && this.childNodes.length > 0;
6897     },
6898
6899     /**
6900      * Insert node(s) as the last child node of this node.
6901      * @param {Node/Array} node The node or Array of nodes to append
6902      * @return {Node} The appended node if single append, or null if an array was passed
6903      */
6904     appendChild : function(node){
6905         var multi = false;
6906         if(node instanceof Array){
6907             multi = node;
6908         }else if(arguments.length > 1){
6909             multi = arguments;
6910         }
6911         // if passed an array or multiple args do them one by one
6912         if(multi){
6913             for(var i = 0, len = multi.length; i < len; i++) {
6914                 this.appendChild(multi[i]);
6915             }
6916         }else{
6917             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6918                 return false;
6919             }
6920             var index = this.childNodes.length;
6921             var oldParent = node.parentNode;
6922             // it's a move, make sure we move it cleanly
6923             if(oldParent){
6924                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6925                     return false;
6926                 }
6927                 oldParent.removeChild(node);
6928             }
6929             index = this.childNodes.length;
6930             if(index == 0){
6931                 this.setFirstChild(node);
6932             }
6933             this.childNodes.push(node);
6934             node.parentNode = this;
6935             var ps = this.childNodes[index-1];
6936             if(ps){
6937                 node.previousSibling = ps;
6938                 ps.nextSibling = node;
6939             }else{
6940                 node.previousSibling = null;
6941             }
6942             node.nextSibling = null;
6943             this.setLastChild(node);
6944             node.setOwnerTree(this.getOwnerTree());
6945             this.fireEvent("append", this.ownerTree, this, node, index);
6946             if(oldParent){
6947                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6948             }
6949             return node;
6950         }
6951     },
6952
6953     /**
6954      * Removes a child node from this node.
6955      * @param {Node} node The node to remove
6956      * @return {Node} The removed node
6957      */
6958     removeChild : function(node){
6959         var index = this.childNodes.indexOf(node);
6960         if(index == -1){
6961             return false;
6962         }
6963         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6964             return false;
6965         }
6966
6967         // remove it from childNodes collection
6968         this.childNodes.splice(index, 1);
6969
6970         // update siblings
6971         if(node.previousSibling){
6972             node.previousSibling.nextSibling = node.nextSibling;
6973         }
6974         if(node.nextSibling){
6975             node.nextSibling.previousSibling = node.previousSibling;
6976         }
6977
6978         // update child refs
6979         if(this.firstChild == node){
6980             this.setFirstChild(node.nextSibling);
6981         }
6982         if(this.lastChild == node){
6983             this.setLastChild(node.previousSibling);
6984         }
6985
6986         node.setOwnerTree(null);
6987         // clear any references from the node
6988         node.parentNode = null;
6989         node.previousSibling = null;
6990         node.nextSibling = null;
6991         this.fireEvent("remove", this.ownerTree, this, node);
6992         return node;
6993     },
6994
6995     /**
6996      * Inserts the first node before the second node in this nodes childNodes collection.
6997      * @param {Node} node The node to insert
6998      * @param {Node} refNode The node to insert before (if null the node is appended)
6999      * @return {Node} The inserted node
7000      */
7001     insertBefore : function(node, refNode){
7002         if(!refNode){ // like standard Dom, refNode can be null for append
7003             return this.appendChild(node);
7004         }
7005         // nothing to do
7006         if(node == refNode){
7007             return false;
7008         }
7009
7010         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7011             return false;
7012         }
7013         var index = this.childNodes.indexOf(refNode);
7014         var oldParent = node.parentNode;
7015         var refIndex = index;
7016
7017         // when moving internally, indexes will change after remove
7018         if(oldParent == this && this.childNodes.indexOf(node) < index){
7019             refIndex--;
7020         }
7021
7022         // it's a move, make sure we move it cleanly
7023         if(oldParent){
7024             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7025                 return false;
7026             }
7027             oldParent.removeChild(node);
7028         }
7029         if(refIndex == 0){
7030             this.setFirstChild(node);
7031         }
7032         this.childNodes.splice(refIndex, 0, node);
7033         node.parentNode = this;
7034         var ps = this.childNodes[refIndex-1];
7035         if(ps){
7036             node.previousSibling = ps;
7037             ps.nextSibling = node;
7038         }else{
7039             node.previousSibling = null;
7040         }
7041         node.nextSibling = refNode;
7042         refNode.previousSibling = node;
7043         node.setOwnerTree(this.getOwnerTree());
7044         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7045         if(oldParent){
7046             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7047         }
7048         return node;
7049     },
7050
7051     /**
7052      * Returns the child node at the specified index.
7053      * @param {Number} index
7054      * @return {Node}
7055      */
7056     item : function(index){
7057         return this.childNodes[index];
7058     },
7059
7060     /**
7061      * Replaces one child node in this node with another.
7062      * @param {Node} newChild The replacement node
7063      * @param {Node} oldChild The node to replace
7064      * @return {Node} The replaced node
7065      */
7066     replaceChild : function(newChild, oldChild){
7067         this.insertBefore(newChild, oldChild);
7068         this.removeChild(oldChild);
7069         return oldChild;
7070     },
7071
7072     /**
7073      * Returns the index of a child node
7074      * @param {Node} node
7075      * @return {Number} The index of the node or -1 if it was not found
7076      */
7077     indexOf : function(child){
7078         return this.childNodes.indexOf(child);
7079     },
7080
7081     /**
7082      * Returns the tree this node is in.
7083      * @return {Tree}
7084      */
7085     getOwnerTree : function(){
7086         // if it doesn't have one, look for one
7087         if(!this.ownerTree){
7088             var p = this;
7089             while(p){
7090                 if(p.ownerTree){
7091                     this.ownerTree = p.ownerTree;
7092                     break;
7093                 }
7094                 p = p.parentNode;
7095             }
7096         }
7097         return this.ownerTree;
7098     },
7099
7100     /**
7101      * Returns depth of this node (the root node has a depth of 0)
7102      * @return {Number}
7103      */
7104     getDepth : function(){
7105         var depth = 0;
7106         var p = this;
7107         while(p.parentNode){
7108             ++depth;
7109             p = p.parentNode;
7110         }
7111         return depth;
7112     },
7113
7114     // private
7115     setOwnerTree : function(tree){
7116         // if it's move, we need to update everyone
7117         if(tree != this.ownerTree){
7118             if(this.ownerTree){
7119                 this.ownerTree.unregisterNode(this);
7120             }
7121             this.ownerTree = tree;
7122             var cs = this.childNodes;
7123             for(var i = 0, len = cs.length; i < len; i++) {
7124                 cs[i].setOwnerTree(tree);
7125             }
7126             if(tree){
7127                 tree.registerNode(this);
7128             }
7129         }
7130     },
7131
7132     /**
7133      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7134      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7135      * @return {String} The path
7136      */
7137     getPath : function(attr){
7138         attr = attr || "id";
7139         var p = this.parentNode;
7140         var b = [this.attributes[attr]];
7141         while(p){
7142             b.unshift(p.attributes[attr]);
7143             p = p.parentNode;
7144         }
7145         var sep = this.getOwnerTree().pathSeparator;
7146         return sep + b.join(sep);
7147     },
7148
7149     /**
7150      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7151      * function call will be the scope provided or the current node. The arguments to the function
7152      * will be the args provided or the current node. If the function returns false at any point,
7153      * the bubble is stopped.
7154      * @param {Function} fn The function to call
7155      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7156      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7157      */
7158     bubble : function(fn, scope, args){
7159         var p = this;
7160         while(p){
7161             if(fn.call(scope || p, args || p) === false){
7162                 break;
7163             }
7164             p = p.parentNode;
7165         }
7166     },
7167
7168     /**
7169      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7170      * function call will be the scope provided or the current node. The arguments to the function
7171      * will be the args provided or the current node. If the function returns false at any point,
7172      * the cascade is stopped on that branch.
7173      * @param {Function} fn The function to call
7174      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7175      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7176      */
7177     cascade : function(fn, scope, args){
7178         if(fn.call(scope || this, args || this) !== false){
7179             var cs = this.childNodes;
7180             for(var i = 0, len = cs.length; i < len; i++) {
7181                 cs[i].cascade(fn, scope, args);
7182             }
7183         }
7184     },
7185
7186     /**
7187      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7188      * function call will be the scope provided or the current node. The arguments to the function
7189      * will be the args provided or the current node. If the function returns false at any point,
7190      * the iteration stops.
7191      * @param {Function} fn The function to call
7192      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7193      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7194      */
7195     eachChild : function(fn, scope, args){
7196         var cs = this.childNodes;
7197         for(var i = 0, len = cs.length; i < len; i++) {
7198                 if(fn.call(scope || this, args || cs[i]) === false){
7199                     break;
7200                 }
7201         }
7202     },
7203
7204     /**
7205      * Finds the first child that has the attribute with the specified value.
7206      * @param {String} attribute The attribute name
7207      * @param {Mixed} value The value to search for
7208      * @return {Node} The found child or null if none was found
7209      */
7210     findChild : function(attribute, value){
7211         var cs = this.childNodes;
7212         for(var i = 0, len = cs.length; i < len; i++) {
7213                 if(cs[i].attributes[attribute] == value){
7214                     return cs[i];
7215                 }
7216         }
7217         return null;
7218     },
7219
7220     /**
7221      * Finds the first child by a custom function. The child matches if the function passed
7222      * returns true.
7223      * @param {Function} fn
7224      * @param {Object} scope (optional)
7225      * @return {Node} The found child or null if none was found
7226      */
7227     findChildBy : function(fn, scope){
7228         var cs = this.childNodes;
7229         for(var i = 0, len = cs.length; i < len; i++) {
7230                 if(fn.call(scope||cs[i], cs[i]) === true){
7231                     return cs[i];
7232                 }
7233         }
7234         return null;
7235     },
7236
7237     /**
7238      * Sorts this nodes children using the supplied sort function
7239      * @param {Function} fn
7240      * @param {Object} scope (optional)
7241      */
7242     sort : function(fn, scope){
7243         var cs = this.childNodes;
7244         var len = cs.length;
7245         if(len > 0){
7246             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7247             cs.sort(sortFn);
7248             for(var i = 0; i < len; i++){
7249                 var n = cs[i];
7250                 n.previousSibling = cs[i-1];
7251                 n.nextSibling = cs[i+1];
7252                 if(i == 0){
7253                     this.setFirstChild(n);
7254                 }
7255                 if(i == len-1){
7256                     this.setLastChild(n);
7257                 }
7258             }
7259         }
7260     },
7261
7262     /**
7263      * Returns true if this node is an ancestor (at any point) of the passed node.
7264      * @param {Node} node
7265      * @return {Boolean}
7266      */
7267     contains : function(node){
7268         return node.isAncestor(this);
7269     },
7270
7271     /**
7272      * Returns true if the passed node is an ancestor (at any point) of this node.
7273      * @param {Node} node
7274      * @return {Boolean}
7275      */
7276     isAncestor : function(node){
7277         var p = this.parentNode;
7278         while(p){
7279             if(p == node){
7280                 return true;
7281             }
7282             p = p.parentNode;
7283         }
7284         return false;
7285     },
7286
7287     toString : function(){
7288         return "[Node"+(this.id?" "+this.id:"")+"]";
7289     }
7290 });/*
7291  * Based on:
7292  * Ext JS Library 1.1.1
7293  * Copyright(c) 2006-2007, Ext JS, LLC.
7294  *
7295  * Originally Released Under LGPL - original licence link has changed is not relivant.
7296  *
7297  * Fork - LGPL
7298  * <script type="text/javascript">
7299  */
7300  (function(){ 
7301 /**
7302  * @class Roo.Layer
7303  * @extends Roo.Element
7304  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7305  * automatic maintaining of shadow/shim positions.
7306  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7307  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7308  * you can pass a string with a CSS class name. False turns off the shadow.
7309  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7310  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7311  * @cfg {String} cls CSS class to add to the element
7312  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7313  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7314  * @constructor
7315  * @param {Object} config An object with config options.
7316  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7317  */
7318
7319 Roo.Layer = function(config, existingEl){
7320     config = config || {};
7321     var dh = Roo.DomHelper;
7322     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7323     if(existingEl){
7324         this.dom = Roo.getDom(existingEl);
7325     }
7326     if(!this.dom){
7327         var o = config.dh || {tag: "div", cls: "x-layer"};
7328         this.dom = dh.append(pel, o);
7329     }
7330     if(config.cls){
7331         this.addClass(config.cls);
7332     }
7333     this.constrain = config.constrain !== false;
7334     this.visibilityMode = Roo.Element.VISIBILITY;
7335     if(config.id){
7336         this.id = this.dom.id = config.id;
7337     }else{
7338         this.id = Roo.id(this.dom);
7339     }
7340     this.zindex = config.zindex || this.getZIndex();
7341     this.position("absolute", this.zindex);
7342     if(config.shadow){
7343         this.shadowOffset = config.shadowOffset || 4;
7344         this.shadow = new Roo.Shadow({
7345             offset : this.shadowOffset,
7346             mode : config.shadow
7347         });
7348     }else{
7349         this.shadowOffset = 0;
7350     }
7351     this.useShim = config.shim !== false && Roo.useShims;
7352     this.useDisplay = config.useDisplay;
7353     this.hide();
7354 };
7355
7356 var supr = Roo.Element.prototype;
7357
7358 // shims are shared among layer to keep from having 100 iframes
7359 var shims = [];
7360
7361 Roo.extend(Roo.Layer, Roo.Element, {
7362
7363     getZIndex : function(){
7364         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7365     },
7366
7367     getShim : function(){
7368         if(!this.useShim){
7369             return null;
7370         }
7371         if(this.shim){
7372             return this.shim;
7373         }
7374         var shim = shims.shift();
7375         if(!shim){
7376             shim = this.createShim();
7377             shim.enableDisplayMode('block');
7378             shim.dom.style.display = 'none';
7379             shim.dom.style.visibility = 'visible';
7380         }
7381         var pn = this.dom.parentNode;
7382         if(shim.dom.parentNode != pn){
7383             pn.insertBefore(shim.dom, this.dom);
7384         }
7385         shim.setStyle('z-index', this.getZIndex()-2);
7386         this.shim = shim;
7387         return shim;
7388     },
7389
7390     hideShim : function(){
7391         if(this.shim){
7392             this.shim.setDisplayed(false);
7393             shims.push(this.shim);
7394             delete this.shim;
7395         }
7396     },
7397
7398     disableShadow : function(){
7399         if(this.shadow){
7400             this.shadowDisabled = true;
7401             this.shadow.hide();
7402             this.lastShadowOffset = this.shadowOffset;
7403             this.shadowOffset = 0;
7404         }
7405     },
7406
7407     enableShadow : function(show){
7408         if(this.shadow){
7409             this.shadowDisabled = false;
7410             this.shadowOffset = this.lastShadowOffset;
7411             delete this.lastShadowOffset;
7412             if(show){
7413                 this.sync(true);
7414             }
7415         }
7416     },
7417
7418     // private
7419     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7420     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7421     sync : function(doShow){
7422         var sw = this.shadow;
7423         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7424             var sh = this.getShim();
7425
7426             var w = this.getWidth(),
7427                 h = this.getHeight();
7428
7429             var l = this.getLeft(true),
7430                 t = this.getTop(true);
7431
7432             if(sw && !this.shadowDisabled){
7433                 if(doShow && !sw.isVisible()){
7434                     sw.show(this);
7435                 }else{
7436                     sw.realign(l, t, w, h);
7437                 }
7438                 if(sh){
7439                     if(doShow){
7440                        sh.show();
7441                     }
7442                     // fit the shim behind the shadow, so it is shimmed too
7443                     var a = sw.adjusts, s = sh.dom.style;
7444                     s.left = (Math.min(l, l+a.l))+"px";
7445                     s.top = (Math.min(t, t+a.t))+"px";
7446                     s.width = (w+a.w)+"px";
7447                     s.height = (h+a.h)+"px";
7448                 }
7449             }else if(sh){
7450                 if(doShow){
7451                    sh.show();
7452                 }
7453                 sh.setSize(w, h);
7454                 sh.setLeftTop(l, t);
7455             }
7456             
7457         }
7458     },
7459
7460     // private
7461     destroy : function(){
7462         this.hideShim();
7463         if(this.shadow){
7464             this.shadow.hide();
7465         }
7466         this.removeAllListeners();
7467         var pn = this.dom.parentNode;
7468         if(pn){
7469             pn.removeChild(this.dom);
7470         }
7471         Roo.Element.uncache(this.id);
7472     },
7473
7474     remove : function(){
7475         this.destroy();
7476     },
7477
7478     // private
7479     beginUpdate : function(){
7480         this.updating = true;
7481     },
7482
7483     // private
7484     endUpdate : function(){
7485         this.updating = false;
7486         this.sync(true);
7487     },
7488
7489     // private
7490     hideUnders : function(negOffset){
7491         if(this.shadow){
7492             this.shadow.hide();
7493         }
7494         this.hideShim();
7495     },
7496
7497     // private
7498     constrainXY : function(){
7499         if(this.constrain){
7500             var vw = Roo.lib.Dom.getViewWidth(),
7501                 vh = Roo.lib.Dom.getViewHeight();
7502             var s = Roo.get(document).getScroll();
7503
7504             var xy = this.getXY();
7505             var x = xy[0], y = xy[1];   
7506             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7507             // only move it if it needs it
7508             var moved = false;
7509             // first validate right/bottom
7510             if((x + w) > vw+s.left){
7511                 x = vw - w - this.shadowOffset;
7512                 moved = true;
7513             }
7514             if((y + h) > vh+s.top){
7515                 y = vh - h - this.shadowOffset;
7516                 moved = true;
7517             }
7518             // then make sure top/left isn't negative
7519             if(x < s.left){
7520                 x = s.left;
7521                 moved = true;
7522             }
7523             if(y < s.top){
7524                 y = s.top;
7525                 moved = true;
7526             }
7527             if(moved){
7528                 if(this.avoidY){
7529                     var ay = this.avoidY;
7530                     if(y <= ay && (y+h) >= ay){
7531                         y = ay-h-5;   
7532                     }
7533                 }
7534                 xy = [x, y];
7535                 this.storeXY(xy);
7536                 supr.setXY.call(this, xy);
7537                 this.sync();
7538             }
7539         }
7540     },
7541
7542     isVisible : function(){
7543         return this.visible;    
7544     },
7545
7546     // private
7547     showAction : function(){
7548         this.visible = true; // track visibility to prevent getStyle calls
7549         if(this.useDisplay === true){
7550             this.setDisplayed("");
7551         }else if(this.lastXY){
7552             supr.setXY.call(this, this.lastXY);
7553         }else if(this.lastLT){
7554             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7555         }
7556     },
7557
7558     // private
7559     hideAction : function(){
7560         this.visible = false;
7561         if(this.useDisplay === true){
7562             this.setDisplayed(false);
7563         }else{
7564             this.setLeftTop(-10000,-10000);
7565         }
7566     },
7567
7568     // overridden Element method
7569     setVisible : function(v, a, d, c, e){
7570         if(v){
7571             this.showAction();
7572         }
7573         if(a && v){
7574             var cb = function(){
7575                 this.sync(true);
7576                 if(c){
7577                     c();
7578                 }
7579             }.createDelegate(this);
7580             supr.setVisible.call(this, true, true, d, cb, e);
7581         }else{
7582             if(!v){
7583                 this.hideUnders(true);
7584             }
7585             var cb = c;
7586             if(a){
7587                 cb = function(){
7588                     this.hideAction();
7589                     if(c){
7590                         c();
7591                     }
7592                 }.createDelegate(this);
7593             }
7594             supr.setVisible.call(this, v, a, d, cb, e);
7595             if(v){
7596                 this.sync(true);
7597             }else if(!a){
7598                 this.hideAction();
7599             }
7600         }
7601     },
7602
7603     storeXY : function(xy){
7604         delete this.lastLT;
7605         this.lastXY = xy;
7606     },
7607
7608     storeLeftTop : function(left, top){
7609         delete this.lastXY;
7610         this.lastLT = [left, top];
7611     },
7612
7613     // private
7614     beforeFx : function(){
7615         this.beforeAction();
7616         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7617     },
7618
7619     // private
7620     afterFx : function(){
7621         Roo.Layer.superclass.afterFx.apply(this, arguments);
7622         this.sync(this.isVisible());
7623     },
7624
7625     // private
7626     beforeAction : function(){
7627         if(!this.updating && this.shadow){
7628             this.shadow.hide();
7629         }
7630     },
7631
7632     // overridden Element method
7633     setLeft : function(left){
7634         this.storeLeftTop(left, this.getTop(true));
7635         supr.setLeft.apply(this, arguments);
7636         this.sync();
7637     },
7638
7639     setTop : function(top){
7640         this.storeLeftTop(this.getLeft(true), top);
7641         supr.setTop.apply(this, arguments);
7642         this.sync();
7643     },
7644
7645     setLeftTop : function(left, top){
7646         this.storeLeftTop(left, top);
7647         supr.setLeftTop.apply(this, arguments);
7648         this.sync();
7649     },
7650
7651     setXY : function(xy, a, d, c, e){
7652         this.fixDisplay();
7653         this.beforeAction();
7654         this.storeXY(xy);
7655         var cb = this.createCB(c);
7656         supr.setXY.call(this, xy, a, d, cb, e);
7657         if(!a){
7658             cb();
7659         }
7660     },
7661
7662     // private
7663     createCB : function(c){
7664         var el = this;
7665         return function(){
7666             el.constrainXY();
7667             el.sync(true);
7668             if(c){
7669                 c();
7670             }
7671         };
7672     },
7673
7674     // overridden Element method
7675     setX : function(x, a, d, c, e){
7676         this.setXY([x, this.getY()], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setY : function(y, a, d, c, e){
7681         this.setXY([this.getX(), y], a, d, c, e);
7682     },
7683
7684     // overridden Element method
7685     setSize : function(w, h, a, d, c, e){
7686         this.beforeAction();
7687         var cb = this.createCB(c);
7688         supr.setSize.call(this, w, h, a, d, cb, e);
7689         if(!a){
7690             cb();
7691         }
7692     },
7693
7694     // overridden Element method
7695     setWidth : function(w, a, d, c, e){
7696         this.beforeAction();
7697         var cb = this.createCB(c);
7698         supr.setWidth.call(this, w, a, d, cb, e);
7699         if(!a){
7700             cb();
7701         }
7702     },
7703
7704     // overridden Element method
7705     setHeight : function(h, a, d, c, e){
7706         this.beforeAction();
7707         var cb = this.createCB(c);
7708         supr.setHeight.call(this, h, a, d, cb, e);
7709         if(!a){
7710             cb();
7711         }
7712     },
7713
7714     // overridden Element method
7715     setBounds : function(x, y, w, h, a, d, c, e){
7716         this.beforeAction();
7717         var cb = this.createCB(c);
7718         if(!a){
7719             this.storeXY([x, y]);
7720             supr.setXY.call(this, [x, y]);
7721             supr.setSize.call(this, w, h, a, d, cb, e);
7722             cb();
7723         }else{
7724             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7725         }
7726         return this;
7727     },
7728     
7729     /**
7730      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7731      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7732      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7733      * @param {Number} zindex The new z-index to set
7734      * @return {this} The Layer
7735      */
7736     setZIndex : function(zindex){
7737         this.zindex = zindex;
7738         this.setStyle("z-index", zindex + 2);
7739         if(this.shadow){
7740             this.shadow.setZIndex(zindex + 1);
7741         }
7742         if(this.shim){
7743             this.shim.setStyle("z-index", zindex);
7744         }
7745     }
7746 });
7747 })();/*
7748  * Based on:
7749  * Ext JS Library 1.1.1
7750  * Copyright(c) 2006-2007, Ext JS, LLC.
7751  *
7752  * Originally Released Under LGPL - original licence link has changed is not relivant.
7753  *
7754  * Fork - LGPL
7755  * <script type="text/javascript">
7756  */
7757
7758
7759 /**
7760  * @class Roo.Shadow
7761  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7762  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7763  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7764  * @constructor
7765  * Create a new Shadow
7766  * @param {Object} config The config object
7767  */
7768 Roo.Shadow = function(config){
7769     Roo.apply(this, config);
7770     if(typeof this.mode != "string"){
7771         this.mode = this.defaultMode;
7772     }
7773     var o = this.offset, a = {h: 0};
7774     var rad = Math.floor(this.offset/2);
7775     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7776         case "drop":
7777             a.w = 0;
7778             a.l = a.t = o;
7779             a.t -= 1;
7780             if(Roo.isIE){
7781                 a.l -= this.offset + rad;
7782                 a.t -= this.offset + rad;
7783                 a.w -= rad;
7784                 a.h -= rad;
7785                 a.t += 1;
7786             }
7787         break;
7788         case "sides":
7789             a.w = (o*2);
7790             a.l = -o;
7791             a.t = o-1;
7792             if(Roo.isIE){
7793                 a.l -= (this.offset - rad);
7794                 a.t -= this.offset + rad;
7795                 a.l += 1;
7796                 a.w -= (this.offset - rad)*2;
7797                 a.w -= rad + 1;
7798                 a.h -= 1;
7799             }
7800         break;
7801         case "frame":
7802             a.w = a.h = (o*2);
7803             a.l = a.t = -o;
7804             a.t += 1;
7805             a.h -= 2;
7806             if(Roo.isIE){
7807                 a.l -= (this.offset - rad);
7808                 a.t -= (this.offset - rad);
7809                 a.l += 1;
7810                 a.w -= (this.offset + rad + 1);
7811                 a.h -= (this.offset + rad);
7812                 a.h += 1;
7813             }
7814         break;
7815     };
7816
7817     this.adjusts = a;
7818 };
7819
7820 Roo.Shadow.prototype = {
7821     /**
7822      * @cfg {String} mode
7823      * The shadow display mode.  Supports the following options:<br />
7824      * sides: Shadow displays on both sides and bottom only<br />
7825      * frame: Shadow displays equally on all four sides<br />
7826      * drop: Traditional bottom-right drop shadow (default)
7827      */
7828     /**
7829      * @cfg {String} offset
7830      * The number of pixels to offset the shadow from the element (defaults to 4)
7831      */
7832     offset: 4,
7833
7834     // private
7835     defaultMode: "drop",
7836
7837     /**
7838      * Displays the shadow under the target element
7839      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7840      */
7841     show : function(target){
7842         target = Roo.get(target);
7843         if(!this.el){
7844             this.el = Roo.Shadow.Pool.pull();
7845             if(this.el.dom.nextSibling != target.dom){
7846                 this.el.insertBefore(target);
7847             }
7848         }
7849         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7850         if(Roo.isIE){
7851             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7852         }
7853         this.realign(
7854             target.getLeft(true),
7855             target.getTop(true),
7856             target.getWidth(),
7857             target.getHeight()
7858         );
7859         this.el.dom.style.display = "block";
7860     },
7861
7862     /**
7863      * Returns true if the shadow is visible, else false
7864      */
7865     isVisible : function(){
7866         return this.el ? true : false;  
7867     },
7868
7869     /**
7870      * Direct alignment when values are already available. Show must be called at least once before
7871      * calling this method to ensure it is initialized.
7872      * @param {Number} left The target element left position
7873      * @param {Number} top The target element top position
7874      * @param {Number} width The target element width
7875      * @param {Number} height The target element height
7876      */
7877     realign : function(l, t, w, h){
7878         if(!this.el){
7879             return;
7880         }
7881         var a = this.adjusts, d = this.el.dom, s = d.style;
7882         var iea = 0;
7883         s.left = (l+a.l)+"px";
7884         s.top = (t+a.t)+"px";
7885         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7886  
7887         if(s.width != sws || s.height != shs){
7888             s.width = sws;
7889             s.height = shs;
7890             if(!Roo.isIE){
7891                 var cn = d.childNodes;
7892                 var sww = Math.max(0, (sw-12))+"px";
7893                 cn[0].childNodes[1].style.width = sww;
7894                 cn[1].childNodes[1].style.width = sww;
7895                 cn[2].childNodes[1].style.width = sww;
7896                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7897             }
7898         }
7899     },
7900
7901     /**
7902      * Hides this shadow
7903      */
7904     hide : function(){
7905         if(this.el){
7906             this.el.dom.style.display = "none";
7907             Roo.Shadow.Pool.push(this.el);
7908             delete this.el;
7909         }
7910     },
7911
7912     /**
7913      * Adjust the z-index of this shadow
7914      * @param {Number} zindex The new z-index
7915      */
7916     setZIndex : function(z){
7917         this.zIndex = z;
7918         if(this.el){
7919             this.el.setStyle("z-index", z);
7920         }
7921     }
7922 };
7923
7924 // Private utility class that manages the internal Shadow cache
7925 Roo.Shadow.Pool = function(){
7926     var p = [];
7927     var markup = Roo.isIE ?
7928                  '<div class="x-ie-shadow"></div>' :
7929                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
7930     return {
7931         pull : function(){
7932             var sh = p.shift();
7933             if(!sh){
7934                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7935                 sh.autoBoxAdjust = false;
7936             }
7937             return sh;
7938         },
7939
7940         push : function(sh){
7941             p.push(sh);
7942         }
7943     };
7944 }();/*
7945  * Based on:
7946  * Ext JS Library 1.1.1
7947  * Copyright(c) 2006-2007, Ext JS, LLC.
7948  *
7949  * Originally Released Under LGPL - original licence link has changed is not relivant.
7950  *
7951  * Fork - LGPL
7952  * <script type="text/javascript">
7953  */
7954
7955
7956 /**
7957  * @class Roo.SplitBar
7958  * @extends Roo.util.Observable
7959  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7960  * <br><br>
7961  * Usage:
7962  * <pre><code>
7963 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7964                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7965 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7966 split.minSize = 100;
7967 split.maxSize = 600;
7968 split.animate = true;
7969 split.on('moved', splitterMoved);
7970 </code></pre>
7971  * @constructor
7972  * Create a new SplitBar
7973  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7974  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7975  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7976  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7977                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7978                         position of the SplitBar).
7979  */
7980 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7981     
7982     /** @private */
7983     this.el = Roo.get(dragElement, true);
7984     this.el.dom.unselectable = "on";
7985     /** @private */
7986     this.resizingEl = Roo.get(resizingElement, true);
7987
7988     /**
7989      * @private
7990      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7991      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7992      * @type Number
7993      */
7994     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7995     
7996     /**
7997      * The minimum size of the resizing element. (Defaults to 0)
7998      * @type Number
7999      */
8000     this.minSize = 0;
8001     
8002     /**
8003      * The maximum size of the resizing element. (Defaults to 2000)
8004      * @type Number
8005      */
8006     this.maxSize = 2000;
8007     
8008     /**
8009      * Whether to animate the transition to the new size
8010      * @type Boolean
8011      */
8012     this.animate = false;
8013     
8014     /**
8015      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8016      * @type Boolean
8017      */
8018     this.useShim = false;
8019     
8020     /** @private */
8021     this.shim = null;
8022     
8023     if(!existingProxy){
8024         /** @private */
8025         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8026     }else{
8027         this.proxy = Roo.get(existingProxy).dom;
8028     }
8029     /** @private */
8030     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8031     
8032     /** @private */
8033     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8034     
8035     /** @private */
8036     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8037     
8038     /** @private */
8039     this.dragSpecs = {};
8040     
8041     /**
8042      * @private The adapter to use to positon and resize elements
8043      */
8044     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8045     this.adapter.init(this);
8046     
8047     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8048         /** @private */
8049         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8050         this.el.addClass("x-splitbar-h");
8051     }else{
8052         /** @private */
8053         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8054         this.el.addClass("x-splitbar-v");
8055     }
8056     
8057     this.addEvents({
8058         /**
8059          * @event resize
8060          * Fires when the splitter is moved (alias for {@link #event-moved})
8061          * @param {Roo.SplitBar} this
8062          * @param {Number} newSize the new width or height
8063          */
8064         "resize" : true,
8065         /**
8066          * @event moved
8067          * Fires when the splitter is moved
8068          * @param {Roo.SplitBar} this
8069          * @param {Number} newSize the new width or height
8070          */
8071         "moved" : true,
8072         /**
8073          * @event beforeresize
8074          * Fires before the splitter is dragged
8075          * @param {Roo.SplitBar} this
8076          */
8077         "beforeresize" : true,
8078
8079         "beforeapply" : true
8080     });
8081
8082     Roo.util.Observable.call(this);
8083 };
8084
8085 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8086     onStartProxyDrag : function(x, y){
8087         this.fireEvent("beforeresize", this);
8088         if(!this.overlay){
8089             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8090             o.unselectable();
8091             o.enableDisplayMode("block");
8092             // all splitbars share the same overlay
8093             Roo.SplitBar.prototype.overlay = o;
8094         }
8095         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8096         this.overlay.show();
8097         Roo.get(this.proxy).setDisplayed("block");
8098         var size = this.adapter.getElementSize(this);
8099         this.activeMinSize = this.getMinimumSize();;
8100         this.activeMaxSize = this.getMaximumSize();;
8101         var c1 = size - this.activeMinSize;
8102         var c2 = Math.max(this.activeMaxSize - size, 0);
8103         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8104             this.dd.resetConstraints();
8105             this.dd.setXConstraint(
8106                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8107                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8108             );
8109             this.dd.setYConstraint(0, 0);
8110         }else{
8111             this.dd.resetConstraints();
8112             this.dd.setXConstraint(0, 0);
8113             this.dd.setYConstraint(
8114                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8115                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8116             );
8117          }
8118         this.dragSpecs.startSize = size;
8119         this.dragSpecs.startPoint = [x, y];
8120         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8121     },
8122     
8123     /** 
8124      * @private Called after the drag operation by the DDProxy
8125      */
8126     onEndProxyDrag : function(e){
8127         Roo.get(this.proxy).setDisplayed(false);
8128         var endPoint = Roo.lib.Event.getXY(e);
8129         if(this.overlay){
8130             this.overlay.hide();
8131         }
8132         var newSize;
8133         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8134             newSize = this.dragSpecs.startSize + 
8135                 (this.placement == Roo.SplitBar.LEFT ?
8136                     endPoint[0] - this.dragSpecs.startPoint[0] :
8137                     this.dragSpecs.startPoint[0] - endPoint[0]
8138                 );
8139         }else{
8140             newSize = this.dragSpecs.startSize + 
8141                 (this.placement == Roo.SplitBar.TOP ?
8142                     endPoint[1] - this.dragSpecs.startPoint[1] :
8143                     this.dragSpecs.startPoint[1] - endPoint[1]
8144                 );
8145         }
8146         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8147         if(newSize != this.dragSpecs.startSize){
8148             if(this.fireEvent('beforeapply', this, newSize) !== false){
8149                 this.adapter.setElementSize(this, newSize);
8150                 this.fireEvent("moved", this, newSize);
8151                 this.fireEvent("resize", this, newSize);
8152             }
8153         }
8154     },
8155     
8156     /**
8157      * Get the adapter this SplitBar uses
8158      * @return The adapter object
8159      */
8160     getAdapter : function(){
8161         return this.adapter;
8162     },
8163     
8164     /**
8165      * Set the adapter this SplitBar uses
8166      * @param {Object} adapter A SplitBar adapter object
8167      */
8168     setAdapter : function(adapter){
8169         this.adapter = adapter;
8170         this.adapter.init(this);
8171     },
8172     
8173     /**
8174      * Gets the minimum size for the resizing element
8175      * @return {Number} The minimum size
8176      */
8177     getMinimumSize : function(){
8178         return this.minSize;
8179     },
8180     
8181     /**
8182      * Sets the minimum size for the resizing element
8183      * @param {Number} minSize The minimum size
8184      */
8185     setMinimumSize : function(minSize){
8186         this.minSize = minSize;
8187     },
8188     
8189     /**
8190      * Gets the maximum size for the resizing element
8191      * @return {Number} The maximum size
8192      */
8193     getMaximumSize : function(){
8194         return this.maxSize;
8195     },
8196     
8197     /**
8198      * Sets the maximum size for the resizing element
8199      * @param {Number} maxSize The maximum size
8200      */
8201     setMaximumSize : function(maxSize){
8202         this.maxSize = maxSize;
8203     },
8204     
8205     /**
8206      * Sets the initialize size for the resizing element
8207      * @param {Number} size The initial size
8208      */
8209     setCurrentSize : function(size){
8210         var oldAnimate = this.animate;
8211         this.animate = false;
8212         this.adapter.setElementSize(this, size);
8213         this.animate = oldAnimate;
8214     },
8215     
8216     /**
8217      * Destroy this splitbar. 
8218      * @param {Boolean} removeEl True to remove the element
8219      */
8220     destroy : function(removeEl){
8221         if(this.shim){
8222             this.shim.remove();
8223         }
8224         this.dd.unreg();
8225         this.proxy.parentNode.removeChild(this.proxy);
8226         if(removeEl){
8227             this.el.remove();
8228         }
8229     }
8230 });
8231
8232 /**
8233  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8234  */
8235 Roo.SplitBar.createProxy = function(dir){
8236     var proxy = new Roo.Element(document.createElement("div"));
8237     proxy.unselectable();
8238     var cls = 'x-splitbar-proxy';
8239     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8240     document.body.appendChild(proxy.dom);
8241     return proxy.dom;
8242 };
8243
8244 /** 
8245  * @class Roo.SplitBar.BasicLayoutAdapter
8246  * Default Adapter. It assumes the splitter and resizing element are not positioned
8247  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8248  */
8249 Roo.SplitBar.BasicLayoutAdapter = function(){
8250 };
8251
8252 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8253     // do nothing for now
8254     init : function(s){
8255     
8256     },
8257     /**
8258      * Called before drag operations to get the current size of the resizing element. 
8259      * @param {Roo.SplitBar} s The SplitBar using this adapter
8260      */
8261      getElementSize : function(s){
8262         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8263             return s.resizingEl.getWidth();
8264         }else{
8265             return s.resizingEl.getHeight();
8266         }
8267     },
8268     
8269     /**
8270      * Called after drag operations to set the size of the resizing element.
8271      * @param {Roo.SplitBar} s The SplitBar using this adapter
8272      * @param {Number} newSize The new size to set
8273      * @param {Function} onComplete A function to be invoked when resizing is complete
8274      */
8275     setElementSize : function(s, newSize, onComplete){
8276         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8277             if(!s.animate){
8278                 s.resizingEl.setWidth(newSize);
8279                 if(onComplete){
8280                     onComplete(s, newSize);
8281                 }
8282             }else{
8283                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8284             }
8285         }else{
8286             
8287             if(!s.animate){
8288                 s.resizingEl.setHeight(newSize);
8289                 if(onComplete){
8290                     onComplete(s, newSize);
8291                 }
8292             }else{
8293                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8294             }
8295         }
8296     }
8297 };
8298
8299 /** 
8300  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8301  * @extends Roo.SplitBar.BasicLayoutAdapter
8302  * Adapter that  moves the splitter element to align with the resized sizing element. 
8303  * Used with an absolute positioned SplitBar.
8304  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8305  * document.body, make sure you assign an id to the body element.
8306  */
8307 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8308     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8309     this.container = Roo.get(container);
8310 };
8311
8312 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8313     init : function(s){
8314         this.basic.init(s);
8315     },
8316     
8317     getElementSize : function(s){
8318         return this.basic.getElementSize(s);
8319     },
8320     
8321     setElementSize : function(s, newSize, onComplete){
8322         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8323     },
8324     
8325     moveSplitter : function(s){
8326         var yes = Roo.SplitBar;
8327         switch(s.placement){
8328             case yes.LEFT:
8329                 s.el.setX(s.resizingEl.getRight());
8330                 break;
8331             case yes.RIGHT:
8332                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8333                 break;
8334             case yes.TOP:
8335                 s.el.setY(s.resizingEl.getBottom());
8336                 break;
8337             case yes.BOTTOM:
8338                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8339                 break;
8340         }
8341     }
8342 };
8343
8344 /**
8345  * Orientation constant - Create a vertical SplitBar
8346  * @static
8347  * @type Number
8348  */
8349 Roo.SplitBar.VERTICAL = 1;
8350
8351 /**
8352  * Orientation constant - Create a horizontal SplitBar
8353  * @static
8354  * @type Number
8355  */
8356 Roo.SplitBar.HORIZONTAL = 2;
8357
8358 /**
8359  * Placement constant - The resizing element is to the left of the splitter element
8360  * @static
8361  * @type Number
8362  */
8363 Roo.SplitBar.LEFT = 1;
8364
8365 /**
8366  * Placement constant - The resizing element is to the right of the splitter element
8367  * @static
8368  * @type Number
8369  */
8370 Roo.SplitBar.RIGHT = 2;
8371
8372 /**
8373  * Placement constant - The resizing element is positioned above the splitter element
8374  * @static
8375  * @type Number
8376  */
8377 Roo.SplitBar.TOP = 3;
8378
8379 /**
8380  * Placement constant - The resizing element is positioned under splitter element
8381  * @static
8382  * @type Number
8383  */
8384 Roo.SplitBar.BOTTOM = 4;
8385 /*
8386  * Based on:
8387  * Ext JS Library 1.1.1
8388  * Copyright(c) 2006-2007, Ext JS, LLC.
8389  *
8390  * Originally Released Under LGPL - original licence link has changed is not relivant.
8391  *
8392  * Fork - LGPL
8393  * <script type="text/javascript">
8394  */
8395
8396 /**
8397  * @class Roo.View
8398  * @extends Roo.util.Observable
8399  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8400  * This class also supports single and multi selection modes. <br>
8401  * Create a data model bound view:
8402  <pre><code>
8403  var store = new Roo.data.Store(...);
8404
8405  var view = new Roo.View({
8406     el : "my-element",
8407     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8408  
8409     singleSelect: true,
8410     selectedClass: "ydataview-selected",
8411     store: store
8412  });
8413
8414  // listen for node click?
8415  view.on("click", function(vw, index, node, e){
8416  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8417  });
8418
8419  // load XML data
8420  dataModel.load("foobar.xml");
8421  </code></pre>
8422  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8423  * <br><br>
8424  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8425  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8426  * 
8427  * Note: old style constructor is still suported (container, template, config)
8428  * 
8429  * @constructor
8430  * Create a new View
8431  * @param {Object} config The config object
8432  * 
8433  */
8434 Roo.View = function(config, depreciated_tpl, depreciated_config){
8435     
8436     this.parent = false;
8437     
8438     if (typeof(depreciated_tpl) == 'undefined') {
8439         // new way.. - universal constructor.
8440         Roo.apply(this, config);
8441         this.el  = Roo.get(this.el);
8442     } else {
8443         // old format..
8444         this.el  = Roo.get(config);
8445         this.tpl = depreciated_tpl;
8446         Roo.apply(this, depreciated_config);
8447     }
8448     this.wrapEl  = this.el.wrap().wrap();
8449     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8450     
8451     
8452     if(typeof(this.tpl) == "string"){
8453         this.tpl = new Roo.Template(this.tpl);
8454     } else {
8455         // support xtype ctors..
8456         this.tpl = new Roo.factory(this.tpl, Roo);
8457     }
8458     
8459     
8460     this.tpl.compile();
8461     
8462     /** @private */
8463     this.addEvents({
8464         /**
8465          * @event beforeclick
8466          * Fires before a click is processed. Returns false to cancel the default action.
8467          * @param {Roo.View} this
8468          * @param {Number} index The index of the target node
8469          * @param {HTMLElement} node The target node
8470          * @param {Roo.EventObject} e The raw event object
8471          */
8472             "beforeclick" : true,
8473         /**
8474          * @event click
8475          * Fires when a template node is clicked.
8476          * @param {Roo.View} this
8477          * @param {Number} index The index of the target node
8478          * @param {HTMLElement} node The target node
8479          * @param {Roo.EventObject} e The raw event object
8480          */
8481             "click" : true,
8482         /**
8483          * @event dblclick
8484          * Fires when a template node is double clicked.
8485          * @param {Roo.View} this
8486          * @param {Number} index The index of the target node
8487          * @param {HTMLElement} node The target node
8488          * @param {Roo.EventObject} e The raw event object
8489          */
8490             "dblclick" : true,
8491         /**
8492          * @event contextmenu
8493          * Fires when a template node is right clicked.
8494          * @param {Roo.View} this
8495          * @param {Number} index The index of the target node
8496          * @param {HTMLElement} node The target node
8497          * @param {Roo.EventObject} e The raw event object
8498          */
8499             "contextmenu" : true,
8500         /**
8501          * @event selectionchange
8502          * Fires when the selected nodes change.
8503          * @param {Roo.View} this
8504          * @param {Array} selections Array of the selected nodes
8505          */
8506             "selectionchange" : true,
8507     
8508         /**
8509          * @event beforeselect
8510          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8511          * @param {Roo.View} this
8512          * @param {HTMLElement} node The node to be selected
8513          * @param {Array} selections Array of currently selected nodes
8514          */
8515             "beforeselect" : true,
8516         /**
8517          * @event preparedata
8518          * Fires on every row to render, to allow you to change the data.
8519          * @param {Roo.View} this
8520          * @param {Object} data to be rendered (change this)
8521          */
8522           "preparedata" : true
8523           
8524           
8525         });
8526
8527
8528
8529     this.el.on({
8530         "click": this.onClick,
8531         "dblclick": this.onDblClick,
8532         "contextmenu": this.onContextMenu,
8533         scope:this
8534     });
8535
8536     this.selections = [];
8537     this.nodes = [];
8538     this.cmp = new Roo.CompositeElementLite([]);
8539     if(this.store){
8540         this.store = Roo.factory(this.store, Roo.data);
8541         this.setStore(this.store, true);
8542     }
8543     
8544     if ( this.footer && this.footer.xtype) {
8545            
8546          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8547         
8548         this.footer.dataSource = this.store;
8549         this.footer.container = fctr;
8550         this.footer = Roo.factory(this.footer, Roo);
8551         fctr.insertFirst(this.el);
8552         
8553         // this is a bit insane - as the paging toolbar seems to detach the el..
8554 //        dom.parentNode.parentNode.parentNode
8555          // they get detached?
8556     }
8557     
8558     
8559     Roo.View.superclass.constructor.call(this);
8560     
8561     
8562 };
8563
8564 Roo.extend(Roo.View, Roo.util.Observable, {
8565     
8566      /**
8567      * @cfg {Roo.data.Store} store Data store to load data from.
8568      */
8569     store : false,
8570     
8571     /**
8572      * @cfg {String|Roo.Element} el The container element.
8573      */
8574     el : '',
8575     
8576     /**
8577      * @cfg {String|Roo.Template} tpl The template used by this View 
8578      */
8579     tpl : false,
8580     /**
8581      * @cfg {String} dataName the named area of the template to use as the data area
8582      *                          Works with domtemplates roo-name="name"
8583      */
8584     dataName: false,
8585     /**
8586      * @cfg {String} selectedClass The css class to add to selected nodes
8587      */
8588     selectedClass : "x-view-selected",
8589      /**
8590      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8591      */
8592     emptyText : "",
8593     
8594     /**
8595      * @cfg {String} text to display on mask (default Loading)
8596      */
8597     mask : false,
8598     /**
8599      * @cfg {Boolean} multiSelect Allow multiple selection
8600      */
8601     multiSelect : false,
8602     /**
8603      * @cfg {Boolean} singleSelect Allow single selection
8604      */
8605     singleSelect:  false,
8606     
8607     /**
8608      * @cfg {Boolean} toggleSelect - selecting 
8609      */
8610     toggleSelect : false,
8611     
8612     /**
8613      * @cfg {Boolean} tickable - selecting 
8614      */
8615     tickable : false,
8616     
8617     /**
8618      * Returns the element this view is bound to.
8619      * @return {Roo.Element}
8620      */
8621     getEl : function(){
8622         return this.wrapEl;
8623     },
8624     
8625     
8626
8627     /**
8628      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8629      */
8630     refresh : function(){
8631         //Roo.log('refresh');
8632         var t = this.tpl;
8633         
8634         // if we are using something like 'domtemplate', then
8635         // the what gets used is:
8636         // t.applySubtemplate(NAME, data, wrapping data..)
8637         // the outer template then get' applied with
8638         //     the store 'extra data'
8639         // and the body get's added to the
8640         //      roo-name="data" node?
8641         //      <span class='roo-tpl-{name}'></span> ?????
8642         
8643         
8644         
8645         this.clearSelections();
8646         this.el.update("");
8647         var html = [];
8648         var records = this.store.getRange();
8649         if(records.length < 1) {
8650             
8651             // is this valid??  = should it render a template??
8652             
8653             this.el.update(this.emptyText);
8654             return;
8655         }
8656         var el = this.el;
8657         if (this.dataName) {
8658             this.el.update(t.apply(this.store.meta)); //????
8659             el = this.el.child('.roo-tpl-' + this.dataName);
8660         }
8661         
8662         for(var i = 0, len = records.length; i < len; i++){
8663             var data = this.prepareData(records[i].data, i, records[i]);
8664             this.fireEvent("preparedata", this, data, i, records[i]);
8665             
8666             var d = Roo.apply({}, data);
8667             
8668             if(this.tickable){
8669                 Roo.apply(d, {'roo-id' : Roo.id()});
8670                 
8671                 var _this = this;
8672             
8673                 Roo.each(this.parent.item, function(item){
8674                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8675                         return;
8676                     }
8677                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8678                 });
8679             }
8680             
8681             html[html.length] = Roo.util.Format.trim(
8682                 this.dataName ?
8683                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8684                     t.apply(d)
8685             );
8686         }
8687         
8688         
8689         
8690         el.update(html.join(""));
8691         this.nodes = el.dom.childNodes;
8692         this.updateIndexes(0);
8693     },
8694     
8695
8696     /**
8697      * Function to override to reformat the data that is sent to
8698      * the template for each node.
8699      * DEPRICATED - use the preparedata event handler.
8700      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8701      * a JSON object for an UpdateManager bound view).
8702      */
8703     prepareData : function(data, index, record)
8704     {
8705         this.fireEvent("preparedata", this, data, index, record);
8706         return data;
8707     },
8708
8709     onUpdate : function(ds, record){
8710         // Roo.log('on update');   
8711         this.clearSelections();
8712         var index = this.store.indexOf(record);
8713         var n = this.nodes[index];
8714         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8715         n.parentNode.removeChild(n);
8716         this.updateIndexes(index, index);
8717     },
8718
8719     
8720     
8721 // --------- FIXME     
8722     onAdd : function(ds, records, index)
8723     {
8724         //Roo.log(['on Add', ds, records, index] );        
8725         this.clearSelections();
8726         if(this.nodes.length == 0){
8727             this.refresh();
8728             return;
8729         }
8730         var n = this.nodes[index];
8731         for(var i = 0, len = records.length; i < len; i++){
8732             var d = this.prepareData(records[i].data, i, records[i]);
8733             if(n){
8734                 this.tpl.insertBefore(n, d);
8735             }else{
8736                 
8737                 this.tpl.append(this.el, d);
8738             }
8739         }
8740         this.updateIndexes(index);
8741     },
8742
8743     onRemove : function(ds, record, index){
8744        // Roo.log('onRemove');
8745         this.clearSelections();
8746         var el = this.dataName  ?
8747             this.el.child('.roo-tpl-' + this.dataName) :
8748             this.el; 
8749         
8750         el.dom.removeChild(this.nodes[index]);
8751         this.updateIndexes(index);
8752     },
8753
8754     /**
8755      * Refresh an individual node.
8756      * @param {Number} index
8757      */
8758     refreshNode : function(index){
8759         this.onUpdate(this.store, this.store.getAt(index));
8760     },
8761
8762     updateIndexes : function(startIndex, endIndex){
8763         var ns = this.nodes;
8764         startIndex = startIndex || 0;
8765         endIndex = endIndex || ns.length - 1;
8766         for(var i = startIndex; i <= endIndex; i++){
8767             ns[i].nodeIndex = i;
8768         }
8769     },
8770
8771     /**
8772      * Changes the data store this view uses and refresh the view.
8773      * @param {Store} store
8774      */
8775     setStore : function(store, initial){
8776         if(!initial && this.store){
8777             this.store.un("datachanged", this.refresh);
8778             this.store.un("add", this.onAdd);
8779             this.store.un("remove", this.onRemove);
8780             this.store.un("update", this.onUpdate);
8781             this.store.un("clear", this.refresh);
8782             this.store.un("beforeload", this.onBeforeLoad);
8783             this.store.un("load", this.onLoad);
8784             this.store.un("loadexception", this.onLoad);
8785         }
8786         if(store){
8787           
8788             store.on("datachanged", this.refresh, this);
8789             store.on("add", this.onAdd, this);
8790             store.on("remove", this.onRemove, this);
8791             store.on("update", this.onUpdate, this);
8792             store.on("clear", this.refresh, this);
8793             store.on("beforeload", this.onBeforeLoad, this);
8794             store.on("load", this.onLoad, this);
8795             store.on("loadexception", this.onLoad, this);
8796         }
8797         
8798         if(store){
8799             this.refresh();
8800         }
8801     },
8802     /**
8803      * onbeforeLoad - masks the loading area.
8804      *
8805      */
8806     onBeforeLoad : function(store,opts)
8807     {
8808          //Roo.log('onBeforeLoad');   
8809         if (!opts.add) {
8810             this.el.update("");
8811         }
8812         this.el.mask(this.mask ? this.mask : "Loading" ); 
8813     },
8814     onLoad : function ()
8815     {
8816         this.el.unmask();
8817     },
8818     
8819
8820     /**
8821      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8822      * @param {HTMLElement} node
8823      * @return {HTMLElement} The template node
8824      */
8825     findItemFromChild : function(node){
8826         var el = this.dataName  ?
8827             this.el.child('.roo-tpl-' + this.dataName,true) :
8828             this.el.dom; 
8829         
8830         if(!node || node.parentNode == el){
8831                     return node;
8832             }
8833             var p = node.parentNode;
8834             while(p && p != el){
8835             if(p.parentNode == el){
8836                 return p;
8837             }
8838             p = p.parentNode;
8839         }
8840             return null;
8841     },
8842
8843     /** @ignore */
8844     onClick : function(e){
8845         var item = this.findItemFromChild(e.getTarget());
8846         if(item){
8847             var index = this.indexOf(item);
8848             if(this.onItemClick(item, index, e) !== false){
8849                 this.fireEvent("click", this, index, item, e);
8850             }
8851         }else{
8852             this.clearSelections();
8853         }
8854     },
8855
8856     /** @ignore */
8857     onContextMenu : function(e){
8858         var item = this.findItemFromChild(e.getTarget());
8859         if(item){
8860             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8861         }
8862     },
8863
8864     /** @ignore */
8865     onDblClick : function(e){
8866         var item = this.findItemFromChild(e.getTarget());
8867         if(item){
8868             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8869         }
8870     },
8871
8872     onItemClick : function(item, index, e)
8873     {
8874         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8875             return false;
8876         }
8877         if (this.toggleSelect) {
8878             var m = this.isSelected(item) ? 'unselect' : 'select';
8879             //Roo.log(m);
8880             var _t = this;
8881             _t[m](item, true, false);
8882             return true;
8883         }
8884         if(this.multiSelect || this.singleSelect){
8885             if(this.multiSelect && e.shiftKey && this.lastSelection){
8886                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8887             }else{
8888                 this.select(item, this.multiSelect && e.ctrlKey);
8889                 this.lastSelection = item;
8890             }
8891             
8892             if(!this.tickable){
8893                 e.preventDefault();
8894             }
8895             
8896         }
8897         return true;
8898     },
8899
8900     /**
8901      * Get the number of selected nodes.
8902      * @return {Number}
8903      */
8904     getSelectionCount : function(){
8905         return this.selections.length;
8906     },
8907
8908     /**
8909      * Get the currently selected nodes.
8910      * @return {Array} An array of HTMLElements
8911      */
8912     getSelectedNodes : function(){
8913         return this.selections;
8914     },
8915
8916     /**
8917      * Get the indexes of the selected nodes.
8918      * @return {Array}
8919      */
8920     getSelectedIndexes : function(){
8921         var indexes = [], s = this.selections;
8922         for(var i = 0, len = s.length; i < len; i++){
8923             indexes.push(s[i].nodeIndex);
8924         }
8925         return indexes;
8926     },
8927
8928     /**
8929      * Clear all selections
8930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8931      */
8932     clearSelections : function(suppressEvent){
8933         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8934             this.cmp.elements = this.selections;
8935             this.cmp.removeClass(this.selectedClass);
8936             this.selections = [];
8937             if(!suppressEvent){
8938                 this.fireEvent("selectionchange", this, this.selections);
8939             }
8940         }
8941     },
8942
8943     /**
8944      * Returns true if the passed node is selected
8945      * @param {HTMLElement/Number} node The node or node index
8946      * @return {Boolean}
8947      */
8948     isSelected : function(node){
8949         var s = this.selections;
8950         if(s.length < 1){
8951             return false;
8952         }
8953         node = this.getNode(node);
8954         return s.indexOf(node) !== -1;
8955     },
8956
8957     /**
8958      * Selects nodes.
8959      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8960      * @param {Boolean} keepExisting (optional) true to keep existing selections
8961      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8962      */
8963     select : function(nodeInfo, keepExisting, suppressEvent){
8964         if(nodeInfo instanceof Array){
8965             if(!keepExisting){
8966                 this.clearSelections(true);
8967             }
8968             for(var i = 0, len = nodeInfo.length; i < len; i++){
8969                 this.select(nodeInfo[i], true, true);
8970             }
8971             return;
8972         } 
8973         var node = this.getNode(nodeInfo);
8974         if(!node || this.isSelected(node)){
8975             return; // already selected.
8976         }
8977         if(!keepExisting){
8978             this.clearSelections(true);
8979         }
8980         
8981         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8982             Roo.fly(node).addClass(this.selectedClass);
8983             this.selections.push(node);
8984             if(!suppressEvent){
8985                 this.fireEvent("selectionchange", this, this.selections);
8986             }
8987         }
8988         
8989         
8990     },
8991       /**
8992      * Unselects nodes.
8993      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8994      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8995      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8996      */
8997     unselect : function(nodeInfo, keepExisting, suppressEvent)
8998     {
8999         if(nodeInfo instanceof Array){
9000             Roo.each(this.selections, function(s) {
9001                 this.unselect(s, nodeInfo);
9002             }, this);
9003             return;
9004         }
9005         var node = this.getNode(nodeInfo);
9006         if(!node || !this.isSelected(node)){
9007             //Roo.log("not selected");
9008             return; // not selected.
9009         }
9010         // fireevent???
9011         var ns = [];
9012         Roo.each(this.selections, function(s) {
9013             if (s == node ) {
9014                 Roo.fly(node).removeClass(this.selectedClass);
9015
9016                 return;
9017             }
9018             ns.push(s);
9019         },this);
9020         
9021         this.selections= ns;
9022         this.fireEvent("selectionchange", this, this.selections);
9023     },
9024
9025     /**
9026      * Gets a template node.
9027      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9028      * @return {HTMLElement} The node or null if it wasn't found
9029      */
9030     getNode : function(nodeInfo){
9031         if(typeof nodeInfo == "string"){
9032             return document.getElementById(nodeInfo);
9033         }else if(typeof nodeInfo == "number"){
9034             return this.nodes[nodeInfo];
9035         }
9036         return nodeInfo;
9037     },
9038
9039     /**
9040      * Gets a range template nodes.
9041      * @param {Number} startIndex
9042      * @param {Number} endIndex
9043      * @return {Array} An array of nodes
9044      */
9045     getNodes : function(start, end){
9046         var ns = this.nodes;
9047         start = start || 0;
9048         end = typeof end == "undefined" ? ns.length - 1 : end;
9049         var nodes = [];
9050         if(start <= end){
9051             for(var i = start; i <= end; i++){
9052                 nodes.push(ns[i]);
9053             }
9054         } else{
9055             for(var i = start; i >= end; i--){
9056                 nodes.push(ns[i]);
9057             }
9058         }
9059         return nodes;
9060     },
9061
9062     /**
9063      * Finds the index of the passed node
9064      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9065      * @return {Number} The index of the node or -1
9066      */
9067     indexOf : function(node){
9068         node = this.getNode(node);
9069         if(typeof node.nodeIndex == "number"){
9070             return node.nodeIndex;
9071         }
9072         var ns = this.nodes;
9073         for(var i = 0, len = ns.length; i < len; i++){
9074             if(ns[i] == node){
9075                 return i;
9076             }
9077         }
9078         return -1;
9079     }
9080 });
9081 /*
9082  * Based on:
9083  * Ext JS Library 1.1.1
9084  * Copyright(c) 2006-2007, Ext JS, LLC.
9085  *
9086  * Originally Released Under LGPL - original licence link has changed is not relivant.
9087  *
9088  * Fork - LGPL
9089  * <script type="text/javascript">
9090  */
9091
9092 /**
9093  * @class Roo.JsonView
9094  * @extends Roo.View
9095  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9096 <pre><code>
9097 var view = new Roo.JsonView({
9098     container: "my-element",
9099     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9100     multiSelect: true, 
9101     jsonRoot: "data" 
9102 });
9103
9104 // listen for node click?
9105 view.on("click", function(vw, index, node, e){
9106     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9107 });
9108
9109 // direct load of JSON data
9110 view.load("foobar.php");
9111
9112 // Example from my blog list
9113 var tpl = new Roo.Template(
9114     '&lt;div class="entry"&gt;' +
9115     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9116     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9117     "&lt;/div&gt;&lt;hr /&gt;"
9118 );
9119
9120 var moreView = new Roo.JsonView({
9121     container :  "entry-list", 
9122     template : tpl,
9123     jsonRoot: "posts"
9124 });
9125 moreView.on("beforerender", this.sortEntries, this);
9126 moreView.load({
9127     url: "/blog/get-posts.php",
9128     params: "allposts=true",
9129     text: "Loading Blog Entries..."
9130 });
9131 </code></pre>
9132
9133 * Note: old code is supported with arguments : (container, template, config)
9134
9135
9136  * @constructor
9137  * Create a new JsonView
9138  * 
9139  * @param {Object} config The config object
9140  * 
9141  */
9142 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9143     
9144     
9145     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9146
9147     var um = this.el.getUpdateManager();
9148     um.setRenderer(this);
9149     um.on("update", this.onLoad, this);
9150     um.on("failure", this.onLoadException, this);
9151
9152     /**
9153      * @event beforerender
9154      * Fires before rendering of the downloaded JSON data.
9155      * @param {Roo.JsonView} this
9156      * @param {Object} data The JSON data loaded
9157      */
9158     /**
9159      * @event load
9160      * Fires when data is loaded.
9161      * @param {Roo.JsonView} this
9162      * @param {Object} data The JSON data loaded
9163      * @param {Object} response The raw Connect response object
9164      */
9165     /**
9166      * @event loadexception
9167      * Fires when loading fails.
9168      * @param {Roo.JsonView} this
9169      * @param {Object} response The raw Connect response object
9170      */
9171     this.addEvents({
9172         'beforerender' : true,
9173         'load' : true,
9174         'loadexception' : true
9175     });
9176 };
9177 Roo.extend(Roo.JsonView, Roo.View, {
9178     /**
9179      * @type {String} The root property in the loaded JSON object that contains the data
9180      */
9181     jsonRoot : "",
9182
9183     /**
9184      * Refreshes the view.
9185      */
9186     refresh : function(){
9187         this.clearSelections();
9188         this.el.update("");
9189         var html = [];
9190         var o = this.jsonData;
9191         if(o && o.length > 0){
9192             for(var i = 0, len = o.length; i < len; i++){
9193                 var data = this.prepareData(o[i], i, o);
9194                 html[html.length] = this.tpl.apply(data);
9195             }
9196         }else{
9197             html.push(this.emptyText);
9198         }
9199         this.el.update(html.join(""));
9200         this.nodes = this.el.dom.childNodes;
9201         this.updateIndexes(0);
9202     },
9203
9204     /**
9205      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9206      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9207      <pre><code>
9208      view.load({
9209          url: "your-url.php",
9210          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9211          callback: yourFunction,
9212          scope: yourObject, //(optional scope)
9213          discardUrl: false,
9214          nocache: false,
9215          text: "Loading...",
9216          timeout: 30,
9217          scripts: false
9218      });
9219      </code></pre>
9220      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9221      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9222      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9223      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9224      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9225      */
9226     load : function(){
9227         var um = this.el.getUpdateManager();
9228         um.update.apply(um, arguments);
9229     },
9230
9231     render : function(el, response){
9232         this.clearSelections();
9233         this.el.update("");
9234         var o;
9235         try{
9236             o = Roo.util.JSON.decode(response.responseText);
9237             if(this.jsonRoot){
9238                 
9239                 o = o[this.jsonRoot];
9240             }
9241         } catch(e){
9242         }
9243         /**
9244          * The current JSON data or null
9245          */
9246         this.jsonData = o;
9247         this.beforeRender();
9248         this.refresh();
9249     },
9250
9251 /**
9252  * Get the number of records in the current JSON dataset
9253  * @return {Number}
9254  */
9255     getCount : function(){
9256         return this.jsonData ? this.jsonData.length : 0;
9257     },
9258
9259 /**
9260  * Returns the JSON object for the specified node(s)
9261  * @param {HTMLElement/Array} node The node or an array of nodes
9262  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9263  * you get the JSON object for the node
9264  */
9265     getNodeData : function(node){
9266         if(node instanceof Array){
9267             var data = [];
9268             for(var i = 0, len = node.length; i < len; i++){
9269                 data.push(this.getNodeData(node[i]));
9270             }
9271             return data;
9272         }
9273         return this.jsonData[this.indexOf(node)] || null;
9274     },
9275
9276     beforeRender : function(){
9277         this.snapshot = this.jsonData;
9278         if(this.sortInfo){
9279             this.sort.apply(this, this.sortInfo);
9280         }
9281         this.fireEvent("beforerender", this, this.jsonData);
9282     },
9283
9284     onLoad : function(el, o){
9285         this.fireEvent("load", this, this.jsonData, o);
9286     },
9287
9288     onLoadException : function(el, o){
9289         this.fireEvent("loadexception", this, o);
9290     },
9291
9292 /**
9293  * Filter the data by a specific property.
9294  * @param {String} property A property on your JSON objects
9295  * @param {String/RegExp} value Either string that the property values
9296  * should start with, or a RegExp to test against the property
9297  */
9298     filter : function(property, value){
9299         if(this.jsonData){
9300             var data = [];
9301             var ss = this.snapshot;
9302             if(typeof value == "string"){
9303                 var vlen = value.length;
9304                 if(vlen == 0){
9305                     this.clearFilter();
9306                     return;
9307                 }
9308                 value = value.toLowerCase();
9309                 for(var i = 0, len = ss.length; i < len; i++){
9310                     var o = ss[i];
9311                     if(o[property].substr(0, vlen).toLowerCase() == value){
9312                         data.push(o);
9313                     }
9314                 }
9315             } else if(value.exec){ // regex?
9316                 for(var i = 0, len = ss.length; i < len; i++){
9317                     var o = ss[i];
9318                     if(value.test(o[property])){
9319                         data.push(o);
9320                     }
9321                 }
9322             } else{
9323                 return;
9324             }
9325             this.jsonData = data;
9326             this.refresh();
9327         }
9328     },
9329
9330 /**
9331  * Filter by a function. The passed function will be called with each
9332  * object in the current dataset. If the function returns true the value is kept,
9333  * otherwise it is filtered.
9334  * @param {Function} fn
9335  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9336  */
9337     filterBy : function(fn, scope){
9338         if(this.jsonData){
9339             var data = [];
9340             var ss = this.snapshot;
9341             for(var i = 0, len = ss.length; i < len; i++){
9342                 var o = ss[i];
9343                 if(fn.call(scope || this, o)){
9344                     data.push(o);
9345                 }
9346             }
9347             this.jsonData = data;
9348             this.refresh();
9349         }
9350     },
9351
9352 /**
9353  * Clears the current filter.
9354  */
9355     clearFilter : function(){
9356         if(this.snapshot && this.jsonData != this.snapshot){
9357             this.jsonData = this.snapshot;
9358             this.refresh();
9359         }
9360     },
9361
9362
9363 /**
9364  * Sorts the data for this view and refreshes it.
9365  * @param {String} property A property on your JSON objects to sort on
9366  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9367  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9368  */
9369     sort : function(property, dir, sortType){
9370         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9371         if(this.jsonData){
9372             var p = property;
9373             var dsc = dir && dir.toLowerCase() == "desc";
9374             var f = function(o1, o2){
9375                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9376                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9377                 ;
9378                 if(v1 < v2){
9379                     return dsc ? +1 : -1;
9380                 } else if(v1 > v2){
9381                     return dsc ? -1 : +1;
9382                 } else{
9383                     return 0;
9384                 }
9385             };
9386             this.jsonData.sort(f);
9387             this.refresh();
9388             if(this.jsonData != this.snapshot){
9389                 this.snapshot.sort(f);
9390             }
9391         }
9392     }
9393 });/*
9394  * Based on:
9395  * Ext JS Library 1.1.1
9396  * Copyright(c) 2006-2007, Ext JS, LLC.
9397  *
9398  * Originally Released Under LGPL - original licence link has changed is not relivant.
9399  *
9400  * Fork - LGPL
9401  * <script type="text/javascript">
9402  */
9403  
9404
9405 /**
9406  * @class Roo.ColorPalette
9407  * @extends Roo.Component
9408  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9409  * Here's an example of typical usage:
9410  * <pre><code>
9411 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9412 cp.render('my-div');
9413
9414 cp.on('select', function(palette, selColor){
9415     // do something with selColor
9416 });
9417 </code></pre>
9418  * @constructor
9419  * Create a new ColorPalette
9420  * @param {Object} config The config object
9421  */
9422 Roo.ColorPalette = function(config){
9423     Roo.ColorPalette.superclass.constructor.call(this, config);
9424     this.addEvents({
9425         /**
9426              * @event select
9427              * Fires when a color is selected
9428              * @param {ColorPalette} this
9429              * @param {String} color The 6-digit color hex code (without the # symbol)
9430              */
9431         select: true
9432     });
9433
9434     if(this.handler){
9435         this.on("select", this.handler, this.scope, true);
9436     }
9437 };
9438 Roo.extend(Roo.ColorPalette, Roo.Component, {
9439     /**
9440      * @cfg {String} itemCls
9441      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9442      */
9443     itemCls : "x-color-palette",
9444     /**
9445      * @cfg {String} value
9446      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9447      * the hex codes are case-sensitive.
9448      */
9449     value : null,
9450     clickEvent:'click',
9451     // private
9452     ctype: "Roo.ColorPalette",
9453
9454     /**
9455      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9456      */
9457     allowReselect : false,
9458
9459     /**
9460      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9461      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9462      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9463      * of colors with the width setting until the box is symmetrical.</p>
9464      * <p>You can override individual colors if needed:</p>
9465      * <pre><code>
9466 var cp = new Roo.ColorPalette();
9467 cp.colors[0] = "FF0000";  // change the first box to red
9468 </code></pre>
9469
9470 Or you can provide a custom array of your own for complete control:
9471 <pre><code>
9472 var cp = new Roo.ColorPalette();
9473 cp.colors = ["000000", "993300", "333300"];
9474 </code></pre>
9475      * @type Array
9476      */
9477     colors : [
9478         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9479         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9480         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9481         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9482         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9483     ],
9484
9485     // private
9486     onRender : function(container, position){
9487         var t = new Roo.MasterTemplate(
9488             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9489         );
9490         var c = this.colors;
9491         for(var i = 0, len = c.length; i < len; i++){
9492             t.add([c[i]]);
9493         }
9494         var el = document.createElement("div");
9495         el.className = this.itemCls;
9496         t.overwrite(el);
9497         container.dom.insertBefore(el, position);
9498         this.el = Roo.get(el);
9499         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9500         if(this.clickEvent != 'click'){
9501             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9502         }
9503     },
9504
9505     // private
9506     afterRender : function(){
9507         Roo.ColorPalette.superclass.afterRender.call(this);
9508         if(this.value){
9509             var s = this.value;
9510             this.value = null;
9511             this.select(s);
9512         }
9513     },
9514
9515     // private
9516     handleClick : function(e, t){
9517         e.preventDefault();
9518         if(!this.disabled){
9519             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9520             this.select(c.toUpperCase());
9521         }
9522     },
9523
9524     /**
9525      * Selects the specified color in the palette (fires the select event)
9526      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9527      */
9528     select : function(color){
9529         color = color.replace("#", "");
9530         if(color != this.value || this.allowReselect){
9531             var el = this.el;
9532             if(this.value){
9533                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9534             }
9535             el.child("a.color-"+color).addClass("x-color-palette-sel");
9536             this.value = color;
9537             this.fireEvent("select", this, color);
9538         }
9539     }
9540 });/*
9541  * Based on:
9542  * Ext JS Library 1.1.1
9543  * Copyright(c) 2006-2007, Ext JS, LLC.
9544  *
9545  * Originally Released Under LGPL - original licence link has changed is not relivant.
9546  *
9547  * Fork - LGPL
9548  * <script type="text/javascript">
9549  */
9550  
9551 /**
9552  * @class Roo.DatePicker
9553  * @extends Roo.Component
9554  * Simple date picker class.
9555  * @constructor
9556  * Create a new DatePicker
9557  * @param {Object} config The config object
9558  */
9559 Roo.DatePicker = function(config){
9560     Roo.DatePicker.superclass.constructor.call(this, config);
9561
9562     this.value = config && config.value ?
9563                  config.value.clearTime() : new Date().clearTime();
9564
9565     this.addEvents({
9566         /**
9567              * @event select
9568              * Fires when a date is selected
9569              * @param {DatePicker} this
9570              * @param {Date} date The selected date
9571              */
9572         'select': true,
9573         /**
9574              * @event monthchange
9575              * Fires when the displayed month changes 
9576              * @param {DatePicker} this
9577              * @param {Date} date The selected month
9578              */
9579         'monthchange': true
9580     });
9581
9582     if(this.handler){
9583         this.on("select", this.handler,  this.scope || this);
9584     }
9585     // build the disabledDatesRE
9586     if(!this.disabledDatesRE && this.disabledDates){
9587         var dd = this.disabledDates;
9588         var re = "(?:";
9589         for(var i = 0; i < dd.length; i++){
9590             re += dd[i];
9591             if(i != dd.length-1) {
9592                 re += "|";
9593             }
9594         }
9595         this.disabledDatesRE = new RegExp(re + ")");
9596     }
9597 };
9598
9599 Roo.extend(Roo.DatePicker, Roo.Component, {
9600     /**
9601      * @cfg {String} todayText
9602      * The text to display on the button that selects the current date (defaults to "Today")
9603      */
9604     todayText : "Today",
9605     /**
9606      * @cfg {String} okText
9607      * The text to display on the ok button
9608      */
9609     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9610     /**
9611      * @cfg {String} cancelText
9612      * The text to display on the cancel button
9613      */
9614     cancelText : "Cancel",
9615     /**
9616      * @cfg {String} todayTip
9617      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9618      */
9619     todayTip : "{0} (Spacebar)",
9620     /**
9621      * @cfg {Date} minDate
9622      * Minimum allowable date (JavaScript date object, defaults to null)
9623      */
9624     minDate : null,
9625     /**
9626      * @cfg {Date} maxDate
9627      * Maximum allowable date (JavaScript date object, defaults to null)
9628      */
9629     maxDate : null,
9630     /**
9631      * @cfg {String} minText
9632      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9633      */
9634     minText : "This date is before the minimum date",
9635     /**
9636      * @cfg {String} maxText
9637      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9638      */
9639     maxText : "This date is after the maximum date",
9640     /**
9641      * @cfg {String} format
9642      * The default date format string which can be overriden for localization support.  The format must be
9643      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9644      */
9645     format : "m/d/y",
9646     /**
9647      * @cfg {Array} disabledDays
9648      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9649      */
9650     disabledDays : null,
9651     /**
9652      * @cfg {String} disabledDaysText
9653      * The tooltip to display when the date falls on a disabled day (defaults to "")
9654      */
9655     disabledDaysText : "",
9656     /**
9657      * @cfg {RegExp} disabledDatesRE
9658      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9659      */
9660     disabledDatesRE : null,
9661     /**
9662      * @cfg {String} disabledDatesText
9663      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9664      */
9665     disabledDatesText : "",
9666     /**
9667      * @cfg {Boolean} constrainToViewport
9668      * True to constrain the date picker to the viewport (defaults to true)
9669      */
9670     constrainToViewport : true,
9671     /**
9672      * @cfg {Array} monthNames
9673      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9674      */
9675     monthNames : Date.monthNames,
9676     /**
9677      * @cfg {Array} dayNames
9678      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9679      */
9680     dayNames : Date.dayNames,
9681     /**
9682      * @cfg {String} nextText
9683      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9684      */
9685     nextText: 'Next Month (Control+Right)',
9686     /**
9687      * @cfg {String} prevText
9688      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9689      */
9690     prevText: 'Previous Month (Control+Left)',
9691     /**
9692      * @cfg {String} monthYearText
9693      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9694      */
9695     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9696     /**
9697      * @cfg {Number} startDay
9698      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9699      */
9700     startDay : 0,
9701     /**
9702      * @cfg {Bool} showClear
9703      * Show a clear button (usefull for date form elements that can be blank.)
9704      */
9705     
9706     showClear: false,
9707     
9708     /**
9709      * Sets the value of the date field
9710      * @param {Date} value The date to set
9711      */
9712     setValue : function(value){
9713         var old = this.value;
9714         
9715         if (typeof(value) == 'string') {
9716          
9717             value = Date.parseDate(value, this.format);
9718         }
9719         if (!value) {
9720             value = new Date();
9721         }
9722         
9723         this.value = value.clearTime(true);
9724         if(this.el){
9725             this.update(this.value);
9726         }
9727     },
9728
9729     /**
9730      * Gets the current selected value of the date field
9731      * @return {Date} The selected date
9732      */
9733     getValue : function(){
9734         return this.value;
9735     },
9736
9737     // private
9738     focus : function(){
9739         if(this.el){
9740             this.update(this.activeDate);
9741         }
9742     },
9743
9744     // privateval
9745     onRender : function(container, position){
9746         
9747         var m = [
9748              '<table cellspacing="0">',
9749                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
9750                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9751         var dn = this.dayNames;
9752         for(var i = 0; i < 7; i++){
9753             var d = this.startDay+i;
9754             if(d > 6){
9755                 d = d-7;
9756             }
9757             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9758         }
9759         m[m.length] = "</tr></thead><tbody><tr>";
9760         for(var i = 0; i < 42; i++) {
9761             if(i % 7 == 0 && i != 0){
9762                 m[m.length] = "</tr><tr>";
9763             }
9764             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9765         }
9766         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9767             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9768
9769         var el = document.createElement("div");
9770         el.className = "x-date-picker";
9771         el.innerHTML = m.join("");
9772
9773         container.dom.insertBefore(el, position);
9774
9775         this.el = Roo.get(el);
9776         this.eventEl = Roo.get(el.firstChild);
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9779             handler: this.showPrevMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9786             handler: this.showNextMonth,
9787             scope: this,
9788             preventDefault:true,
9789             stopDefault:true
9790         });
9791
9792         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9793
9794         this.monthPicker = this.el.down('div.x-date-mp');
9795         this.monthPicker.enableDisplayMode('block');
9796         
9797         var kn = new Roo.KeyNav(this.eventEl, {
9798             "left" : function(e){
9799                 e.ctrlKey ?
9800                     this.showPrevMonth() :
9801                     this.update(this.activeDate.add("d", -1));
9802             },
9803
9804             "right" : function(e){
9805                 e.ctrlKey ?
9806                     this.showNextMonth() :
9807                     this.update(this.activeDate.add("d", 1));
9808             },
9809
9810             "up" : function(e){
9811                 e.ctrlKey ?
9812                     this.showNextYear() :
9813                     this.update(this.activeDate.add("d", -7));
9814             },
9815
9816             "down" : function(e){
9817                 e.ctrlKey ?
9818                     this.showPrevYear() :
9819                     this.update(this.activeDate.add("d", 7));
9820             },
9821
9822             "pageUp" : function(e){
9823                 this.showNextMonth();
9824             },
9825
9826             "pageDown" : function(e){
9827                 this.showPrevMonth();
9828             },
9829
9830             "enter" : function(e){
9831                 e.stopPropagation();
9832                 return true;
9833             },
9834
9835             scope : this
9836         });
9837
9838         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9839
9840         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9841
9842         this.el.unselectable();
9843         
9844         this.cells = this.el.select("table.x-date-inner tbody td");
9845         this.textNodes = this.el.query("table.x-date-inner tbody span");
9846
9847         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9848             text: "&#160;",
9849             tooltip: this.monthYearText
9850         });
9851
9852         this.mbtn.on('click', this.showMonthPicker, this);
9853         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9854
9855
9856         var today = (new Date()).dateFormat(this.format);
9857         
9858         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9859         if (this.showClear) {
9860             baseTb.add( new Roo.Toolbar.Fill());
9861         }
9862         baseTb.add({
9863             text: String.format(this.todayText, today),
9864             tooltip: String.format(this.todayTip, today),
9865             handler: this.selectToday,
9866             scope: this
9867         });
9868         
9869         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9870             
9871         //});
9872         if (this.showClear) {
9873             
9874             baseTb.add( new Roo.Toolbar.Fill());
9875             baseTb.add({
9876                 text: '&#160;',
9877                 cls: 'x-btn-icon x-btn-clear',
9878                 handler: function() {
9879                     //this.value = '';
9880                     this.fireEvent("select", this, '');
9881                 },
9882                 scope: this
9883             });
9884         }
9885         
9886         
9887         if(Roo.isIE){
9888             this.el.repaint();
9889         }
9890         this.update(this.value);
9891     },
9892
9893     createMonthPicker : function(){
9894         if(!this.monthPicker.dom.firstChild){
9895             var buf = ['<table border="0" cellspacing="0">'];
9896             for(var i = 0; i < 6; i++){
9897                 buf.push(
9898                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9899                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9900                     i == 0 ?
9901                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
9902                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9903                 );
9904             }
9905             buf.push(
9906                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9907                     this.okText,
9908                     '</button><button type="button" class="x-date-mp-cancel">',
9909                     this.cancelText,
9910                     '</button></td></tr>',
9911                 '</table>'
9912             );
9913             this.monthPicker.update(buf.join(''));
9914             this.monthPicker.on('click', this.onMonthClick, this);
9915             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9916
9917             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9918             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9919
9920             this.mpMonths.each(function(m, a, i){
9921                 i += 1;
9922                 if((i%2) == 0){
9923                     m.dom.xmonth = 5 + Math.round(i * .5);
9924                 }else{
9925                     m.dom.xmonth = Math.round((i-1) * .5);
9926                 }
9927             });
9928         }
9929     },
9930
9931     showMonthPicker : function(){
9932         this.createMonthPicker();
9933         var size = this.el.getSize();
9934         this.monthPicker.setSize(size);
9935         this.monthPicker.child('table').setSize(size);
9936
9937         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9938         this.updateMPMonth(this.mpSelMonth);
9939         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9940         this.updateMPYear(this.mpSelYear);
9941
9942         this.monthPicker.slideIn('t', {duration:.2});
9943     },
9944
9945     updateMPYear : function(y){
9946         this.mpyear = y;
9947         var ys = this.mpYears.elements;
9948         for(var i = 1; i <= 10; i++){
9949             var td = ys[i-1], y2;
9950             if((i%2) == 0){
9951                 y2 = y + Math.round(i * .5);
9952                 td.firstChild.innerHTML = y2;
9953                 td.xyear = y2;
9954             }else{
9955                 y2 = y - (5-Math.round(i * .5));
9956                 td.firstChild.innerHTML = y2;
9957                 td.xyear = y2;
9958             }
9959             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9960         }
9961     },
9962
9963     updateMPMonth : function(sm){
9964         this.mpMonths.each(function(m, a, i){
9965             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9966         });
9967     },
9968
9969     selectMPMonth: function(m){
9970         
9971     },
9972
9973     onMonthClick : function(e, t){
9974         e.stopEvent();
9975         var el = new Roo.Element(t), pn;
9976         if(el.is('button.x-date-mp-cancel')){
9977             this.hideMonthPicker();
9978         }
9979         else if(el.is('button.x-date-mp-ok')){
9980             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9981             this.hideMonthPicker();
9982         }
9983         else if(pn = el.up('td.x-date-mp-month', 2)){
9984             this.mpMonths.removeClass('x-date-mp-sel');
9985             pn.addClass('x-date-mp-sel');
9986             this.mpSelMonth = pn.dom.xmonth;
9987         }
9988         else if(pn = el.up('td.x-date-mp-year', 2)){
9989             this.mpYears.removeClass('x-date-mp-sel');
9990             pn.addClass('x-date-mp-sel');
9991             this.mpSelYear = pn.dom.xyear;
9992         }
9993         else if(el.is('a.x-date-mp-prev')){
9994             this.updateMPYear(this.mpyear-10);
9995         }
9996         else if(el.is('a.x-date-mp-next')){
9997             this.updateMPYear(this.mpyear+10);
9998         }
9999     },
10000
10001     onMonthDblClick : function(e, t){
10002         e.stopEvent();
10003         var el = new Roo.Element(t), pn;
10004         if(pn = el.up('td.x-date-mp-month', 2)){
10005             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10006             this.hideMonthPicker();
10007         }
10008         else if(pn = el.up('td.x-date-mp-year', 2)){
10009             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10010             this.hideMonthPicker();
10011         }
10012     },
10013
10014     hideMonthPicker : function(disableAnim){
10015         if(this.monthPicker){
10016             if(disableAnim === true){
10017                 this.monthPicker.hide();
10018             }else{
10019                 this.monthPicker.slideOut('t', {duration:.2});
10020             }
10021         }
10022     },
10023
10024     // private
10025     showPrevMonth : function(e){
10026         this.update(this.activeDate.add("mo", -1));
10027     },
10028
10029     // private
10030     showNextMonth : function(e){
10031         this.update(this.activeDate.add("mo", 1));
10032     },
10033
10034     // private
10035     showPrevYear : function(){
10036         this.update(this.activeDate.add("y", -1));
10037     },
10038
10039     // private
10040     showNextYear : function(){
10041         this.update(this.activeDate.add("y", 1));
10042     },
10043
10044     // private
10045     handleMouseWheel : function(e){
10046         var delta = e.getWheelDelta();
10047         if(delta > 0){
10048             this.showPrevMonth();
10049             e.stopEvent();
10050         } else if(delta < 0){
10051             this.showNextMonth();
10052             e.stopEvent();
10053         }
10054     },
10055
10056     // private
10057     handleDateClick : function(e, t){
10058         e.stopEvent();
10059         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10060             this.setValue(new Date(t.dateValue));
10061             this.fireEvent("select", this, this.value);
10062         }
10063     },
10064
10065     // private
10066     selectToday : function(){
10067         this.setValue(new Date().clearTime());
10068         this.fireEvent("select", this, this.value);
10069     },
10070
10071     // private
10072     update : function(date)
10073     {
10074         var vd = this.activeDate;
10075         this.activeDate = date;
10076         if(vd && this.el){
10077             var t = date.getTime();
10078             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10079                 this.cells.removeClass("x-date-selected");
10080                 this.cells.each(function(c){
10081                    if(c.dom.firstChild.dateValue == t){
10082                        c.addClass("x-date-selected");
10083                        setTimeout(function(){
10084                             try{c.dom.firstChild.focus();}catch(e){}
10085                        }, 50);
10086                        return false;
10087                    }
10088                 });
10089                 return;
10090             }
10091         }
10092         
10093         var days = date.getDaysInMonth();
10094         var firstOfMonth = date.getFirstDateOfMonth();
10095         var startingPos = firstOfMonth.getDay()-this.startDay;
10096
10097         if(startingPos <= this.startDay){
10098             startingPos += 7;
10099         }
10100
10101         var pm = date.add("mo", -1);
10102         var prevStart = pm.getDaysInMonth()-startingPos;
10103
10104         var cells = this.cells.elements;
10105         var textEls = this.textNodes;
10106         days += startingPos;
10107
10108         // convert everything to numbers so it's fast
10109         var day = 86400000;
10110         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10111         var today = new Date().clearTime().getTime();
10112         var sel = date.clearTime().getTime();
10113         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10114         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10115         var ddMatch = this.disabledDatesRE;
10116         var ddText = this.disabledDatesText;
10117         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10118         var ddaysText = this.disabledDaysText;
10119         var format = this.format;
10120
10121         var setCellClass = function(cal, cell){
10122             cell.title = "";
10123             var t = d.getTime();
10124             cell.firstChild.dateValue = t;
10125             if(t == today){
10126                 cell.className += " x-date-today";
10127                 cell.title = cal.todayText;
10128             }
10129             if(t == sel){
10130                 cell.className += " x-date-selected";
10131                 setTimeout(function(){
10132                     try{cell.firstChild.focus();}catch(e){}
10133                 }, 50);
10134             }
10135             // disabling
10136             if(t < min) {
10137                 cell.className = " x-date-disabled";
10138                 cell.title = cal.minText;
10139                 return;
10140             }
10141             if(t > max) {
10142                 cell.className = " x-date-disabled";
10143                 cell.title = cal.maxText;
10144                 return;
10145             }
10146             if(ddays){
10147                 if(ddays.indexOf(d.getDay()) != -1){
10148                     cell.title = ddaysText;
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152             if(ddMatch && format){
10153                 var fvalue = d.dateFormat(format);
10154                 if(ddMatch.test(fvalue)){
10155                     cell.title = ddText.replace("%0", fvalue);
10156                     cell.className = " x-date-disabled";
10157                 }
10158             }
10159         };
10160
10161         var i = 0;
10162         for(; i < startingPos; i++) {
10163             textEls[i].innerHTML = (++prevStart);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-prevday";
10166             setCellClass(this, cells[i]);
10167         }
10168         for(; i < days; i++){
10169             intDay = i - startingPos + 1;
10170             textEls[i].innerHTML = (intDay);
10171             d.setDate(d.getDate()+1);
10172             cells[i].className = "x-date-active";
10173             setCellClass(this, cells[i]);
10174         }
10175         var extraDays = 0;
10176         for(; i < 42; i++) {
10177              textEls[i].innerHTML = (++extraDays);
10178              d.setDate(d.getDate()+1);
10179              cells[i].className = "x-date-nextday";
10180              setCellClass(this, cells[i]);
10181         }
10182
10183         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10184         this.fireEvent('monthchange', this, date);
10185         
10186         if(!this.internalRender){
10187             var main = this.el.dom.firstChild;
10188             var w = main.offsetWidth;
10189             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10190             Roo.fly(main).setWidth(w);
10191             this.internalRender = true;
10192             // opera does not respect the auto grow header center column
10193             // then, after it gets a width opera refuses to recalculate
10194             // without a second pass
10195             if(Roo.isOpera && !this.secondPass){
10196                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10197                 this.secondPass = true;
10198                 this.update.defer(10, this, [date]);
10199             }
10200         }
10201         
10202         
10203     }
10204 });        /*
10205  * Based on:
10206  * Ext JS Library 1.1.1
10207  * Copyright(c) 2006-2007, Ext JS, LLC.
10208  *
10209  * Originally Released Under LGPL - original licence link has changed is not relivant.
10210  *
10211  * Fork - LGPL
10212  * <script type="text/javascript">
10213  */
10214 /**
10215  * @class Roo.TabPanel
10216  * @extends Roo.util.Observable
10217  * A lightweight tab container.
10218  * <br><br>
10219  * Usage:
10220  * <pre><code>
10221 // basic tabs 1, built from existing content
10222 var tabs = new Roo.TabPanel("tabs1");
10223 tabs.addTab("script", "View Script");
10224 tabs.addTab("markup", "View Markup");
10225 tabs.activate("script");
10226
10227 // more advanced tabs, built from javascript
10228 var jtabs = new Roo.TabPanel("jtabs");
10229 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10230
10231 // set up the UpdateManager
10232 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10233 var updater = tab2.getUpdateManager();
10234 updater.setDefaultUrl("ajax1.htm");
10235 tab2.on('activate', updater.refresh, updater, true);
10236
10237 // Use setUrl for Ajax loading
10238 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10239 tab3.setUrl("ajax2.htm", null, true);
10240
10241 // Disabled tab
10242 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10243 tab4.disable();
10244
10245 jtabs.activate("jtabs-1");
10246  * </code></pre>
10247  * @constructor
10248  * Create a new TabPanel.
10249  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10250  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10251  */
10252 Roo.TabPanel = function(container, config){
10253     /**
10254     * The container element for this TabPanel.
10255     * @type Roo.Element
10256     */
10257     this.el = Roo.get(container, true);
10258     if(config){
10259         if(typeof config == "boolean"){
10260             this.tabPosition = config ? "bottom" : "top";
10261         }else{
10262             Roo.apply(this, config);
10263         }
10264     }
10265     if(this.tabPosition == "bottom"){
10266         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10267         this.el.addClass("x-tabs-bottom");
10268     }
10269     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10270     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10271     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10272     if(Roo.isIE){
10273         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10274     }
10275     if(this.tabPosition != "bottom"){
10276         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10277          * @type Roo.Element
10278          */
10279         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10280         this.el.addClass("x-tabs-top");
10281     }
10282     this.items = [];
10283
10284     this.bodyEl.setStyle("position", "relative");
10285
10286     this.active = null;
10287     this.activateDelegate = this.activate.createDelegate(this);
10288
10289     this.addEvents({
10290         /**
10291          * @event tabchange
10292          * Fires when the active tab changes
10293          * @param {Roo.TabPanel} this
10294          * @param {Roo.TabPanelItem} activePanel The new active tab
10295          */
10296         "tabchange": true,
10297         /**
10298          * @event beforetabchange
10299          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10300          * @param {Roo.TabPanel} this
10301          * @param {Object} e Set cancel to true on this object to cancel the tab change
10302          * @param {Roo.TabPanelItem} tab The tab being changed to
10303          */
10304         "beforetabchange" : true
10305     });
10306
10307     Roo.EventManager.onWindowResize(this.onResize, this);
10308     this.cpad = this.el.getPadding("lr");
10309     this.hiddenCount = 0;
10310
10311
10312     // toolbar on the tabbar support...
10313     if (this.toolbar) {
10314         var tcfg = this.toolbar;
10315         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10316         this.toolbar = new Roo.Toolbar(tcfg);
10317         if (Roo.isSafari) {
10318             var tbl = tcfg.container.child('table', true);
10319             tbl.setAttribute('width', '100%');
10320         }
10321         
10322     }
10323    
10324
10325
10326     Roo.TabPanel.superclass.constructor.call(this);
10327 };
10328
10329 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10330     /*
10331      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10332      */
10333     tabPosition : "top",
10334     /*
10335      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10336      */
10337     currentTabWidth : 0,
10338     /*
10339      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10340      */
10341     minTabWidth : 40,
10342     /*
10343      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10344      */
10345     maxTabWidth : 250,
10346     /*
10347      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10348      */
10349     preferredTabWidth : 175,
10350     /*
10351      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10352      */
10353     resizeTabs : false,
10354     /*
10355      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10356      */
10357     monitorResize : true,
10358     /*
10359      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10360      */
10361     toolbar : false,
10362
10363     /**
10364      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10365      * @param {String} id The id of the div to use <b>or create</b>
10366      * @param {String} text The text for the tab
10367      * @param {String} content (optional) Content to put in the TabPanelItem body
10368      * @param {Boolean} closable (optional) True to create a close icon on the tab
10369      * @return {Roo.TabPanelItem} The created TabPanelItem
10370      */
10371     addTab : function(id, text, content, closable){
10372         var item = new Roo.TabPanelItem(this, id, text, closable);
10373         this.addTabItem(item);
10374         if(content){
10375             item.setContent(content);
10376         }
10377         return item;
10378     },
10379
10380     /**
10381      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10382      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10383      * @return {Roo.TabPanelItem}
10384      */
10385     getTab : function(id){
10386         return this.items[id];
10387     },
10388
10389     /**
10390      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10391      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10392      */
10393     hideTab : function(id){
10394         var t = this.items[id];
10395         if(!t.isHidden()){
10396            t.setHidden(true);
10397            this.hiddenCount++;
10398            this.autoSizeTabs();
10399         }
10400     },
10401
10402     /**
10403      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10404      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10405      */
10406     unhideTab : function(id){
10407         var t = this.items[id];
10408         if(t.isHidden()){
10409            t.setHidden(false);
10410            this.hiddenCount--;
10411            this.autoSizeTabs();
10412         }
10413     },
10414
10415     /**
10416      * Adds an existing {@link Roo.TabPanelItem}.
10417      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10418      */
10419     addTabItem : function(item){
10420         this.items[item.id] = item;
10421         this.items.push(item);
10422         if(this.resizeTabs){
10423            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10424            this.autoSizeTabs();
10425         }else{
10426             item.autoSize();
10427         }
10428     },
10429
10430     /**
10431      * Removes a {@link Roo.TabPanelItem}.
10432      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10433      */
10434     removeTab : function(id){
10435         var items = this.items;
10436         var tab = items[id];
10437         if(!tab) { return; }
10438         var index = items.indexOf(tab);
10439         if(this.active == tab && items.length > 1){
10440             var newTab = this.getNextAvailable(index);
10441             if(newTab) {
10442                 newTab.activate();
10443             }
10444         }
10445         this.stripEl.dom.removeChild(tab.pnode.dom);
10446         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10447             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10448         }
10449         items.splice(index, 1);
10450         delete this.items[tab.id];
10451         tab.fireEvent("close", tab);
10452         tab.purgeListeners();
10453         this.autoSizeTabs();
10454     },
10455
10456     getNextAvailable : function(start){
10457         var items = this.items;
10458         var index = start;
10459         // look for a next tab that will slide over to
10460         // replace the one being removed
10461         while(index < items.length){
10462             var item = items[++index];
10463             if(item && !item.isHidden()){
10464                 return item;
10465             }
10466         }
10467         // if one isn't found select the previous tab (on the left)
10468         index = start;
10469         while(index >= 0){
10470             var item = items[--index];
10471             if(item && !item.isHidden()){
10472                 return item;
10473             }
10474         }
10475         return null;
10476     },
10477
10478     /**
10479      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10480      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10481      */
10482     disableTab : function(id){
10483         var tab = this.items[id];
10484         if(tab && this.active != tab){
10485             tab.disable();
10486         }
10487     },
10488
10489     /**
10490      * Enables a {@link Roo.TabPanelItem} that is disabled.
10491      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10492      */
10493     enableTab : function(id){
10494         var tab = this.items[id];
10495         tab.enable();
10496     },
10497
10498     /**
10499      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10500      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10501      * @return {Roo.TabPanelItem} The TabPanelItem.
10502      */
10503     activate : function(id){
10504         var tab = this.items[id];
10505         if(!tab){
10506             return null;
10507         }
10508         if(tab == this.active || tab.disabled){
10509             return tab;
10510         }
10511         var e = {};
10512         this.fireEvent("beforetabchange", this, e, tab);
10513         if(e.cancel !== true && !tab.disabled){
10514             if(this.active){
10515                 this.active.hide();
10516             }
10517             this.active = this.items[id];
10518             this.active.show();
10519             this.fireEvent("tabchange", this, this.active);
10520         }
10521         return tab;
10522     },
10523
10524     /**
10525      * Gets the active {@link Roo.TabPanelItem}.
10526      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10527      */
10528     getActiveTab : function(){
10529         return this.active;
10530     },
10531
10532     /**
10533      * Updates the tab body element to fit the height of the container element
10534      * for overflow scrolling
10535      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10536      */
10537     syncHeight : function(targetHeight){
10538         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10539         var bm = this.bodyEl.getMargins();
10540         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10541         this.bodyEl.setHeight(newHeight);
10542         return newHeight;
10543     },
10544
10545     onResize : function(){
10546         if(this.monitorResize){
10547             this.autoSizeTabs();
10548         }
10549     },
10550
10551     /**
10552      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     beginUpdate : function(){
10555         this.updating = true;
10556     },
10557
10558     /**
10559      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10560      */
10561     endUpdate : function(){
10562         this.updating = false;
10563         this.autoSizeTabs();
10564     },
10565
10566     /**
10567      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10568      */
10569     autoSizeTabs : function(){
10570         var count = this.items.length;
10571         var vcount = count - this.hiddenCount;
10572         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
10573             return;
10574         }
10575         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10576         var availWidth = Math.floor(w / vcount);
10577         var b = this.stripBody;
10578         if(b.getWidth() > w){
10579             var tabs = this.items;
10580             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10581             if(availWidth < this.minTabWidth){
10582                 /*if(!this.sleft){    // incomplete scrolling code
10583                     this.createScrollButtons();
10584                 }
10585                 this.showScroll();
10586                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10587             }
10588         }else{
10589             if(this.currentTabWidth < this.preferredTabWidth){
10590                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10591             }
10592         }
10593     },
10594
10595     /**
10596      * Returns the number of tabs in this TabPanel.
10597      * @return {Number}
10598      */
10599      getCount : function(){
10600          return this.items.length;
10601      },
10602
10603     /**
10604      * Resizes all the tabs to the passed width
10605      * @param {Number} The new width
10606      */
10607     setTabWidth : function(width){
10608         this.currentTabWidth = width;
10609         for(var i = 0, len = this.items.length; i < len; i++) {
10610                 if(!this.items[i].isHidden()) {
10611                 this.items[i].setWidth(width);
10612             }
10613         }
10614     },
10615
10616     /**
10617      * Destroys this TabPanel
10618      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10619      */
10620     destroy : function(removeEl){
10621         Roo.EventManager.removeResizeListener(this.onResize, this);
10622         for(var i = 0, len = this.items.length; i < len; i++){
10623             this.items[i].purgeListeners();
10624         }
10625         if(removeEl === true){
10626             this.el.update("");
10627             this.el.remove();
10628         }
10629     }
10630 });
10631
10632 /**
10633  * @class Roo.TabPanelItem
10634  * @extends Roo.util.Observable
10635  * Represents an individual item (tab plus body) in a TabPanel.
10636  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10637  * @param {String} id The id of this TabPanelItem
10638  * @param {String} text The text for the tab of this TabPanelItem
10639  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10640  */
10641 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10642     /**
10643      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10644      * @type Roo.TabPanel
10645      */
10646     this.tabPanel = tabPanel;
10647     /**
10648      * The id for this TabPanelItem
10649      * @type String
10650      */
10651     this.id = id;
10652     /** @private */
10653     this.disabled = false;
10654     /** @private */
10655     this.text = text;
10656     /** @private */
10657     this.loaded = false;
10658     this.closable = closable;
10659
10660     /**
10661      * The body element for this TabPanelItem.
10662      * @type Roo.Element
10663      */
10664     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10665     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10666     this.bodyEl.setStyle("display", "block");
10667     this.bodyEl.setStyle("zoom", "1");
10668     this.hideAction();
10669
10670     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10671     /** @private */
10672     this.el = Roo.get(els.el, true);
10673     this.inner = Roo.get(els.inner, true);
10674     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10675     this.pnode = Roo.get(els.el.parentNode, true);
10676     this.el.on("mousedown", this.onTabMouseDown, this);
10677     this.el.on("click", this.onTabClick, this);
10678     /** @private */
10679     if(closable){
10680         var c = Roo.get(els.close, true);
10681         c.dom.title = this.closeText;
10682         c.addClassOnOver("close-over");
10683         c.on("click", this.closeClick, this);
10684      }
10685
10686     this.addEvents({
10687          /**
10688          * @event activate
10689          * Fires when this tab becomes the active tab.
10690          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10691          * @param {Roo.TabPanelItem} this
10692          */
10693         "activate": true,
10694         /**
10695          * @event beforeclose
10696          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10697          * @param {Roo.TabPanelItem} this
10698          * @param {Object} e Set cancel to true on this object to cancel the close.
10699          */
10700         "beforeclose": true,
10701         /**
10702          * @event close
10703          * Fires when this tab is closed.
10704          * @param {Roo.TabPanelItem} this
10705          */
10706          "close": true,
10707         /**
10708          * @event deactivate
10709          * Fires when this tab is no longer the active tab.
10710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10711          * @param {Roo.TabPanelItem} this
10712          */
10713          "deactivate" : true
10714     });
10715     this.hidden = false;
10716
10717     Roo.TabPanelItem.superclass.constructor.call(this);
10718 };
10719
10720 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10721     purgeListeners : function(){
10722        Roo.util.Observable.prototype.purgeListeners.call(this);
10723        this.el.removeAllListeners();
10724     },
10725     /**
10726      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10727      */
10728     show : function(){
10729         this.pnode.addClass("on");
10730         this.showAction();
10731         if(Roo.isOpera){
10732             this.tabPanel.stripWrap.repaint();
10733         }
10734         this.fireEvent("activate", this.tabPanel, this);
10735     },
10736
10737     /**
10738      * Returns true if this tab is the active tab.
10739      * @return {Boolean}
10740      */
10741     isActive : function(){
10742         return this.tabPanel.getActiveTab() == this;
10743     },
10744
10745     /**
10746      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10747      */
10748     hide : function(){
10749         this.pnode.removeClass("on");
10750         this.hideAction();
10751         this.fireEvent("deactivate", this.tabPanel, this);
10752     },
10753
10754     hideAction : function(){
10755         this.bodyEl.hide();
10756         this.bodyEl.setStyle("position", "absolute");
10757         this.bodyEl.setLeft("-20000px");
10758         this.bodyEl.setTop("-20000px");
10759     },
10760
10761     showAction : function(){
10762         this.bodyEl.setStyle("position", "relative");
10763         this.bodyEl.setTop("");
10764         this.bodyEl.setLeft("");
10765         this.bodyEl.show();
10766     },
10767
10768     /**
10769      * Set the tooltip for the tab.
10770      * @param {String} tooltip The tab's tooltip
10771      */
10772     setTooltip : function(text){
10773         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10774             this.textEl.dom.qtip = text;
10775             this.textEl.dom.removeAttribute('title');
10776         }else{
10777             this.textEl.dom.title = text;
10778         }
10779     },
10780
10781     onTabClick : function(e){
10782         e.preventDefault();
10783         this.tabPanel.activate(this.id);
10784     },
10785
10786     onTabMouseDown : function(e){
10787         e.preventDefault();
10788         this.tabPanel.activate(this.id);
10789     },
10790
10791     getWidth : function(){
10792         return this.inner.getWidth();
10793     },
10794
10795     setWidth : function(width){
10796         var iwidth = width - this.pnode.getPadding("lr");
10797         this.inner.setWidth(iwidth);
10798         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10799         this.pnode.setWidth(width);
10800     },
10801
10802     /**
10803      * Show or hide the tab
10804      * @param {Boolean} hidden True to hide or false to show.
10805      */
10806     setHidden : function(hidden){
10807         this.hidden = hidden;
10808         this.pnode.setStyle("display", hidden ? "none" : "");
10809     },
10810
10811     /**
10812      * Returns true if this tab is "hidden"
10813      * @return {Boolean}
10814      */
10815     isHidden : function(){
10816         return this.hidden;
10817     },
10818
10819     /**
10820      * Returns the text for this tab
10821      * @return {String}
10822      */
10823     getText : function(){
10824         return this.text;
10825     },
10826
10827     autoSize : function(){
10828         //this.el.beginMeasure();
10829         this.textEl.setWidth(1);
10830         /*
10831          *  #2804 [new] Tabs in Roojs
10832          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10833          */
10834         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10835         //this.el.endMeasure();
10836     },
10837
10838     /**
10839      * Sets the text for the tab (Note: this also sets the tooltip text)
10840      * @param {String} text The tab's text and tooltip
10841      */
10842     setText : function(text){
10843         this.text = text;
10844         this.textEl.update(text);
10845         this.setTooltip(text);
10846         if(!this.tabPanel.resizeTabs){
10847             this.autoSize();
10848         }
10849     },
10850     /**
10851      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10852      */
10853     activate : function(){
10854         this.tabPanel.activate(this.id);
10855     },
10856
10857     /**
10858      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10859      */
10860     disable : function(){
10861         if(this.tabPanel.active != this){
10862             this.disabled = true;
10863             this.pnode.addClass("disabled");
10864         }
10865     },
10866
10867     /**
10868      * Enables this TabPanelItem if it was previously disabled.
10869      */
10870     enable : function(){
10871         this.disabled = false;
10872         this.pnode.removeClass("disabled");
10873     },
10874
10875     /**
10876      * Sets the content for this TabPanelItem.
10877      * @param {String} content The content
10878      * @param {Boolean} loadScripts true to look for and load scripts
10879      */
10880     setContent : function(content, loadScripts){
10881         this.bodyEl.update(content, loadScripts);
10882     },
10883
10884     /**
10885      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     getUpdateManager : function(){
10889         return this.bodyEl.getUpdateManager();
10890     },
10891
10892     /**
10893      * Set a URL to be used to load the content for this TabPanelItem.
10894      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10895      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
10896      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
10897      * @return {Roo.UpdateManager} The UpdateManager
10898      */
10899     setUrl : function(url, params, loadOnce){
10900         if(this.refreshDelegate){
10901             this.un('activate', this.refreshDelegate);
10902         }
10903         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10904         this.on("activate", this.refreshDelegate);
10905         return this.bodyEl.getUpdateManager();
10906     },
10907
10908     /** @private */
10909     _handleRefresh : function(url, params, loadOnce){
10910         if(!loadOnce || !this.loaded){
10911             var updater = this.bodyEl.getUpdateManager();
10912             updater.update(url, params, this._setLoaded.createDelegate(this));
10913         }
10914     },
10915
10916     /**
10917      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10918      *   Will fail silently if the setUrl method has not been called.
10919      *   This does not activate the panel, just updates its content.
10920      */
10921     refresh : function(){
10922         if(this.refreshDelegate){
10923            this.loaded = false;
10924            this.refreshDelegate();
10925         }
10926     },
10927
10928     /** @private */
10929     _setLoaded : function(){
10930         this.loaded = true;
10931     },
10932
10933     /** @private */
10934     closeClick : function(e){
10935         var o = {};
10936         e.stopEvent();
10937         this.fireEvent("beforeclose", this, o);
10938         if(o.cancel !== true){
10939             this.tabPanel.removeTab(this.id);
10940         }
10941     },
10942     /**
10943      * The text displayed in the tooltip for the close icon.
10944      * @type String
10945      */
10946     closeText : "Close this tab"
10947 });
10948
10949 /** @private */
10950 Roo.TabPanel.prototype.createStrip = function(container){
10951     var strip = document.createElement("div");
10952     strip.className = "x-tabs-wrap";
10953     container.appendChild(strip);
10954     return strip;
10955 };
10956 /** @private */
10957 Roo.TabPanel.prototype.createStripList = function(strip){
10958     // div wrapper for retard IE
10959     // returns the "tr" element.
10960     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10961         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10962         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10963     return strip.firstChild.firstChild.firstChild.firstChild;
10964 };
10965 /** @private */
10966 Roo.TabPanel.prototype.createBody = function(container){
10967     var body = document.createElement("div");
10968     Roo.id(body, "tab-body");
10969     Roo.fly(body).addClass("x-tabs-body");
10970     container.appendChild(body);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10975     var body = Roo.getDom(id);
10976     if(!body){
10977         body = document.createElement("div");
10978         body.id = id;
10979     }
10980     Roo.fly(body).addClass("x-tabs-item-body");
10981     bodyEl.insertBefore(body, bodyEl.firstChild);
10982     return body;
10983 };
10984 /** @private */
10985 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10986     var td = document.createElement("td");
10987     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10988     //stripEl.appendChild(td);
10989     if(closable){
10990         td.className = "x-tabs-closable";
10991         if(!this.closeTpl){
10992             this.closeTpl = new Roo.Template(
10993                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10994                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10995                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10996             );
10997         }
10998         var el = this.closeTpl.overwrite(td, {"text": text});
10999         var close = el.getElementsByTagName("div")[0];
11000         var inner = el.getElementsByTagName("em")[0];
11001         return {"el": el, "close": close, "inner": inner};
11002     } else {
11003         if(!this.tabTpl){
11004             this.tabTpl = new Roo.Template(
11005                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11006                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11007             );
11008         }
11009         var el = this.tabTpl.overwrite(td, {"text": text});
11010         var inner = el.getElementsByTagName("em")[0];
11011         return {"el": el, "inner": inner};
11012     }
11013 };/*
11014  * Based on:
11015  * Ext JS Library 1.1.1
11016  * Copyright(c) 2006-2007, Ext JS, LLC.
11017  *
11018  * Originally Released Under LGPL - original licence link has changed is not relivant.
11019  *
11020  * Fork - LGPL
11021  * <script type="text/javascript">
11022  */
11023
11024 /**
11025  * @class Roo.Button
11026  * @extends Roo.util.Observable
11027  * Simple Button class
11028  * @cfg {String} text The button text
11029  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11030  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11031  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11032  * @cfg {Object} scope The scope of the handler
11033  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11034  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11035  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11036  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11037  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11038  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11039    applies if enableToggle = true)
11040  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11041  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11042   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11043  * @constructor
11044  * Create a new button
11045  * @param {Object} config The config object
11046  */
11047 Roo.Button = function(renderTo, config)
11048 {
11049     if (!config) {
11050         config = renderTo;
11051         renderTo = config.renderTo || false;
11052     }
11053     
11054     Roo.apply(this, config);
11055     this.addEvents({
11056         /**
11057              * @event click
11058              * Fires when this button is clicked
11059              * @param {Button} this
11060              * @param {EventObject} e The click event
11061              */
11062             "click" : true,
11063         /**
11064              * @event toggle
11065              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11066              * @param {Button} this
11067              * @param {Boolean} pressed
11068              */
11069             "toggle" : true,
11070         /**
11071              * @event mouseover
11072              * Fires when the mouse hovers over the button
11073              * @param {Button} this
11074              * @param {Event} e The event object
11075              */
11076         'mouseover' : true,
11077         /**
11078              * @event mouseout
11079              * Fires when the mouse exits the button
11080              * @param {Button} this
11081              * @param {Event} e The event object
11082              */
11083         'mouseout': true,
11084          /**
11085              * @event render
11086              * Fires when the button is rendered
11087              * @param {Button} this
11088              */
11089         'render': true
11090     });
11091     if(this.menu){
11092         this.menu = Roo.menu.MenuMgr.get(this.menu);
11093     }
11094     // register listeners first!!  - so render can be captured..
11095     Roo.util.Observable.call(this);
11096     if(renderTo){
11097         this.render(renderTo);
11098     }
11099     
11100   
11101 };
11102
11103 Roo.extend(Roo.Button, Roo.util.Observable, {
11104     /**
11105      * 
11106      */
11107     
11108     /**
11109      * Read-only. True if this button is hidden
11110      * @type Boolean
11111      */
11112     hidden : false,
11113     /**
11114      * Read-only. True if this button is disabled
11115      * @type Boolean
11116      */
11117     disabled : false,
11118     /**
11119      * Read-only. True if this button is pressed (only if enableToggle = true)
11120      * @type Boolean
11121      */
11122     pressed : false,
11123
11124     /**
11125      * @cfg {Number} tabIndex 
11126      * The DOM tabIndex for this button (defaults to undefined)
11127      */
11128     tabIndex : undefined,
11129
11130     /**
11131      * @cfg {Boolean} enableToggle
11132      * True to enable pressed/not pressed toggling (defaults to false)
11133      */
11134     enableToggle: false,
11135     /**
11136      * @cfg {Mixed} menu
11137      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11138      */
11139     menu : undefined,
11140     /**
11141      * @cfg {String} menuAlign
11142      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11143      */
11144     menuAlign : "tl-bl?",
11145
11146     /**
11147      * @cfg {String} iconCls
11148      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11149      */
11150     iconCls : undefined,
11151     /**
11152      * @cfg {String} type
11153      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11154      */
11155     type : 'button',
11156
11157     // private
11158     menuClassTarget: 'tr',
11159
11160     /**
11161      * @cfg {String} clickEvent
11162      * The type of event to map to the button's event handler (defaults to 'click')
11163      */
11164     clickEvent : 'click',
11165
11166     /**
11167      * @cfg {Boolean} handleMouseEvents
11168      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11169      */
11170     handleMouseEvents : true,
11171
11172     /**
11173      * @cfg {String} tooltipType
11174      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11175      */
11176     tooltipType : 'qtip',
11177
11178     /**
11179      * @cfg {String} cls
11180      * A CSS class to apply to the button's main element.
11181      */
11182     
11183     /**
11184      * @cfg {Roo.Template} template (Optional)
11185      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11186      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11187      * require code modifications if required elements (e.g. a button) aren't present.
11188      */
11189
11190     // private
11191     render : function(renderTo){
11192         var btn;
11193         if(this.hideParent){
11194             this.parentEl = Roo.get(renderTo);
11195         }
11196         if(!this.dhconfig){
11197             if(!this.template){
11198                 if(!Roo.Button.buttonTemplate){
11199                     // hideous table template
11200                     Roo.Button.buttonTemplate = new Roo.Template(
11201                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11202                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11203                         "</tr></tbody></table>");
11204                 }
11205                 this.template = Roo.Button.buttonTemplate;
11206             }
11207             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11208             var btnEl = btn.child("button:first");
11209             btnEl.on('focus', this.onFocus, this);
11210             btnEl.on('blur', this.onBlur, this);
11211             if(this.cls){
11212                 btn.addClass(this.cls);
11213             }
11214             if(this.icon){
11215                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11216             }
11217             if(this.iconCls){
11218                 btnEl.addClass(this.iconCls);
11219                 if(!this.cls){
11220                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11221                 }
11222             }
11223             if(this.tabIndex !== undefined){
11224                 btnEl.dom.tabIndex = this.tabIndex;
11225             }
11226             if(this.tooltip){
11227                 if(typeof this.tooltip == 'object'){
11228                     Roo.QuickTips.tips(Roo.apply({
11229                           target: btnEl.id
11230                     }, this.tooltip));
11231                 } else {
11232                     btnEl.dom[this.tooltipType] = this.tooltip;
11233                 }
11234             }
11235         }else{
11236             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11237         }
11238         this.el = btn;
11239         if(this.id){
11240             this.el.dom.id = this.el.id = this.id;
11241         }
11242         if(this.menu){
11243             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11244             this.menu.on("show", this.onMenuShow, this);
11245             this.menu.on("hide", this.onMenuHide, this);
11246         }
11247         btn.addClass("x-btn");
11248         if(Roo.isIE && !Roo.isIE7){
11249             this.autoWidth.defer(1, this);
11250         }else{
11251             this.autoWidth();
11252         }
11253         if(this.handleMouseEvents){
11254             btn.on("mouseover", this.onMouseOver, this);
11255             btn.on("mouseout", this.onMouseOut, this);
11256             btn.on("mousedown", this.onMouseDown, this);
11257         }
11258         btn.on(this.clickEvent, this.onClick, this);
11259         //btn.on("mouseup", this.onMouseUp, this);
11260         if(this.hidden){
11261             this.hide();
11262         }
11263         if(this.disabled){
11264             this.disable();
11265         }
11266         Roo.ButtonToggleMgr.register(this);
11267         if(this.pressed){
11268             this.el.addClass("x-btn-pressed");
11269         }
11270         if(this.repeat){
11271             var repeater = new Roo.util.ClickRepeater(btn,
11272                 typeof this.repeat == "object" ? this.repeat : {}
11273             );
11274             repeater.on("click", this.onClick,  this);
11275         }
11276         
11277         this.fireEvent('render', this);
11278         
11279     },
11280     /**
11281      * Returns the button's underlying element
11282      * @return {Roo.Element} The element
11283      */
11284     getEl : function(){
11285         return this.el;  
11286     },
11287     
11288     /**
11289      * Destroys this Button and removes any listeners.
11290      */
11291     destroy : function(){
11292         Roo.ButtonToggleMgr.unregister(this);
11293         this.el.removeAllListeners();
11294         this.purgeListeners();
11295         this.el.remove();
11296     },
11297
11298     // private
11299     autoWidth : function(){
11300         if(this.el){
11301             this.el.setWidth("auto");
11302             if(Roo.isIE7 && Roo.isStrict){
11303                 var ib = this.el.child('button');
11304                 if(ib && ib.getWidth() > 20){
11305                     ib.clip();
11306                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11307                 }
11308             }
11309             if(this.minWidth){
11310                 if(this.hidden){
11311                     this.el.beginMeasure();
11312                 }
11313                 if(this.el.getWidth() < this.minWidth){
11314                     this.el.setWidth(this.minWidth);
11315                 }
11316                 if(this.hidden){
11317                     this.el.endMeasure();
11318                 }
11319             }
11320         }
11321     },
11322
11323     /**
11324      * Assigns this button's click handler
11325      * @param {Function} handler The function to call when the button is clicked
11326      * @param {Object} scope (optional) Scope for the function passed in
11327      */
11328     setHandler : function(handler, scope){
11329         this.handler = handler;
11330         this.scope = scope;  
11331     },
11332     
11333     /**
11334      * Sets this button's text
11335      * @param {String} text The button text
11336      */
11337     setText : function(text){
11338         this.text = text;
11339         if(this.el){
11340             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11341         }
11342         this.autoWidth();
11343     },
11344     
11345     /**
11346      * Gets the text for this button
11347      * @return {String} The button text
11348      */
11349     getText : function(){
11350         return this.text;  
11351     },
11352     
11353     /**
11354      * Show this button
11355      */
11356     show: function(){
11357         this.hidden = false;
11358         if(this.el){
11359             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11360         }
11361     },
11362     
11363     /**
11364      * Hide this button
11365      */
11366     hide: function(){
11367         this.hidden = true;
11368         if(this.el){
11369             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11370         }
11371     },
11372     
11373     /**
11374      * Convenience function for boolean show/hide
11375      * @param {Boolean} visible True to show, false to hide
11376      */
11377     setVisible: function(visible){
11378         if(visible) {
11379             this.show();
11380         }else{
11381             this.hide();
11382         }
11383     },
11384     
11385     /**
11386      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11387      * @param {Boolean} state (optional) Force a particular state
11388      */
11389     toggle : function(state){
11390         state = state === undefined ? !this.pressed : state;
11391         if(state != this.pressed){
11392             if(state){
11393                 this.el.addClass("x-btn-pressed");
11394                 this.pressed = true;
11395                 this.fireEvent("toggle", this, true);
11396             }else{
11397                 this.el.removeClass("x-btn-pressed");
11398                 this.pressed = false;
11399                 this.fireEvent("toggle", this, false);
11400             }
11401             if(this.toggleHandler){
11402                 this.toggleHandler.call(this.scope || this, this, state);
11403             }
11404         }
11405     },
11406     
11407     /**
11408      * Focus the button
11409      */
11410     focus : function(){
11411         this.el.child('button:first').focus();
11412     },
11413     
11414     /**
11415      * Disable this button
11416      */
11417     disable : function(){
11418         if(this.el){
11419             this.el.addClass("x-btn-disabled");
11420         }
11421         this.disabled = true;
11422     },
11423     
11424     /**
11425      * Enable this button
11426      */
11427     enable : function(){
11428         if(this.el){
11429             this.el.removeClass("x-btn-disabled");
11430         }
11431         this.disabled = false;
11432     },
11433
11434     /**
11435      * Convenience function for boolean enable/disable
11436      * @param {Boolean} enabled True to enable, false to disable
11437      */
11438     setDisabled : function(v){
11439         this[v !== true ? "enable" : "disable"]();
11440     },
11441
11442     // private
11443     onClick : function(e)
11444     {
11445         if(e){
11446             e.preventDefault();
11447         }
11448         if(e.button != 0){
11449             return;
11450         }
11451         if(!this.disabled){
11452             if(this.enableToggle){
11453                 this.toggle();
11454             }
11455             if(this.menu && !this.menu.isVisible()){
11456                 this.menu.show(this.el, this.menuAlign);
11457             }
11458             this.fireEvent("click", this, e);
11459             if(this.handler){
11460                 this.el.removeClass("x-btn-over");
11461                 this.handler.call(this.scope || this, this, e);
11462             }
11463         }
11464     },
11465     // private
11466     onMouseOver : function(e){
11467         if(!this.disabled){
11468             this.el.addClass("x-btn-over");
11469             this.fireEvent('mouseover', this, e);
11470         }
11471     },
11472     // private
11473     onMouseOut : function(e){
11474         if(!e.within(this.el,  true)){
11475             this.el.removeClass("x-btn-over");
11476             this.fireEvent('mouseout', this, e);
11477         }
11478     },
11479     // private
11480     onFocus : function(e){
11481         if(!this.disabled){
11482             this.el.addClass("x-btn-focus");
11483         }
11484     },
11485     // private
11486     onBlur : function(e){
11487         this.el.removeClass("x-btn-focus");
11488     },
11489     // private
11490     onMouseDown : function(e){
11491         if(!this.disabled && e.button == 0){
11492             this.el.addClass("x-btn-click");
11493             Roo.get(document).on('mouseup', this.onMouseUp, this);
11494         }
11495     },
11496     // private
11497     onMouseUp : function(e){
11498         if(e.button == 0){
11499             this.el.removeClass("x-btn-click");
11500             Roo.get(document).un('mouseup', this.onMouseUp, this);
11501         }
11502     },
11503     // private
11504     onMenuShow : function(e){
11505         this.el.addClass("x-btn-menu-active");
11506     },
11507     // private
11508     onMenuHide : function(e){
11509         this.el.removeClass("x-btn-menu-active");
11510     }   
11511 });
11512
11513 // Private utility class used by Button
11514 Roo.ButtonToggleMgr = function(){
11515    var groups = {};
11516    
11517    function toggleGroup(btn, state){
11518        if(state){
11519            var g = groups[btn.toggleGroup];
11520            for(var i = 0, l = g.length; i < l; i++){
11521                if(g[i] != btn){
11522                    g[i].toggle(false);
11523                }
11524            }
11525        }
11526    }
11527    
11528    return {
11529        register : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(!g){
11535                g = groups[btn.toggleGroup] = [];
11536            }
11537            g.push(btn);
11538            btn.on("toggle", toggleGroup);
11539        },
11540        
11541        unregister : function(btn){
11542            if(!btn.toggleGroup){
11543                return;
11544            }
11545            var g = groups[btn.toggleGroup];
11546            if(g){
11547                g.remove(btn);
11548                btn.un("toggle", toggleGroup);
11549            }
11550        }
11551    };
11552 }();/*
11553  * Based on:
11554  * Ext JS Library 1.1.1
11555  * Copyright(c) 2006-2007, Ext JS, LLC.
11556  *
11557  * Originally Released Under LGPL - original licence link has changed is not relivant.
11558  *
11559  * Fork - LGPL
11560  * <script type="text/javascript">
11561  */
11562  
11563 /**
11564  * @class Roo.SplitButton
11565  * @extends Roo.Button
11566  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11567  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11568  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11569  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11570  * @cfg {String} arrowTooltip The title attribute of the arrow
11571  * @constructor
11572  * Create a new menu button
11573  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11574  * @param {Object} config The config object
11575  */
11576 Roo.SplitButton = function(renderTo, config){
11577     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11578     /**
11579      * @event arrowclick
11580      * Fires when this button's arrow is clicked
11581      * @param {SplitButton} this
11582      * @param {EventObject} e The click event
11583      */
11584     this.addEvents({"arrowclick":true});
11585 };
11586
11587 Roo.extend(Roo.SplitButton, Roo.Button, {
11588     render : function(renderTo){
11589         // this is one sweet looking template!
11590         var tpl = new Roo.Template(
11591             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11592             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11593             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
11594             "</tbody></table></td><td>",
11595             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11596             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
11597             "</tbody></table></td></tr></table>"
11598         );
11599         var btn = tpl.append(renderTo, [this.text, this.type], true);
11600         var btnEl = btn.child("button");
11601         if(this.cls){
11602             btn.addClass(this.cls);
11603         }
11604         if(this.icon){
11605             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11606         }
11607         if(this.iconCls){
11608             btnEl.addClass(this.iconCls);
11609             if(!this.cls){
11610                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11611             }
11612         }
11613         this.el = btn;
11614         if(this.handleMouseEvents){
11615             btn.on("mouseover", this.onMouseOver, this);
11616             btn.on("mouseout", this.onMouseOut, this);
11617             btn.on("mousedown", this.onMouseDown, this);
11618             btn.on("mouseup", this.onMouseUp, this);
11619         }
11620         btn.on(this.clickEvent, this.onClick, this);
11621         if(this.tooltip){
11622             if(typeof this.tooltip == 'object'){
11623                 Roo.QuickTips.tips(Roo.apply({
11624                       target: btnEl.id
11625                 }, this.tooltip));
11626             } else {
11627                 btnEl.dom[this.tooltipType] = this.tooltip;
11628             }
11629         }
11630         if(this.arrowTooltip){
11631             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11632         }
11633         if(this.hidden){
11634             this.hide();
11635         }
11636         if(this.disabled){
11637             this.disable();
11638         }
11639         if(this.pressed){
11640             this.el.addClass("x-btn-pressed");
11641         }
11642         if(Roo.isIE && !Roo.isIE7){
11643             this.autoWidth.defer(1, this);
11644         }else{
11645             this.autoWidth();
11646         }
11647         if(this.menu){
11648             this.menu.on("show", this.onMenuShow, this);
11649             this.menu.on("hide", this.onMenuHide, this);
11650         }
11651         this.fireEvent('render', this);
11652     },
11653
11654     // private
11655     autoWidth : function(){
11656         if(this.el){
11657             var tbl = this.el.child("table:first");
11658             var tbl2 = this.el.child("table:last");
11659             this.el.setWidth("auto");
11660             tbl.setWidth("auto");
11661             if(Roo.isIE7 && Roo.isStrict){
11662                 var ib = this.el.child('button:first');
11663                 if(ib && ib.getWidth() > 20){
11664                     ib.clip();
11665                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11666                 }
11667             }
11668             if(this.minWidth){
11669                 if(this.hidden){
11670                     this.el.beginMeasure();
11671                 }
11672                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11673                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11674                 }
11675                 if(this.hidden){
11676                     this.el.endMeasure();
11677                 }
11678             }
11679             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11680         } 
11681     },
11682     /**
11683      * Sets this button's click handler
11684      * @param {Function} handler The function to call when the button is clicked
11685      * @param {Object} scope (optional) Scope for the function passed above
11686      */
11687     setHandler : function(handler, scope){
11688         this.handler = handler;
11689         this.scope = scope;  
11690     },
11691     
11692     /**
11693      * Sets this button's arrow click handler
11694      * @param {Function} handler The function to call when the arrow is clicked
11695      * @param {Object} scope (optional) Scope for the function passed above
11696      */
11697     setArrowHandler : function(handler, scope){
11698         this.arrowHandler = handler;
11699         this.scope = scope;  
11700     },
11701     
11702     /**
11703      * Focus the button
11704      */
11705     focus : function(){
11706         if(this.el){
11707             this.el.child("button:first").focus();
11708         }
11709     },
11710
11711     // private
11712     onClick : function(e){
11713         e.preventDefault();
11714         if(!this.disabled){
11715             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11716                 if(this.menu && !this.menu.isVisible()){
11717                     this.menu.show(this.el, this.menuAlign);
11718                 }
11719                 this.fireEvent("arrowclick", this, e);
11720                 if(this.arrowHandler){
11721                     this.arrowHandler.call(this.scope || this, this, e);
11722                 }
11723             }else{
11724                 this.fireEvent("click", this, e);
11725                 if(this.handler){
11726                     this.handler.call(this.scope || this, this, e);
11727                 }
11728             }
11729         }
11730     },
11731     // private
11732     onMouseDown : function(e){
11733         if(!this.disabled){
11734             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11735         }
11736     },
11737     // private
11738     onMouseUp : function(e){
11739         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11740     }   
11741 });
11742
11743
11744 // backwards compat
11745 Roo.MenuButton = Roo.SplitButton;/*
11746  * Based on:
11747  * Ext JS Library 1.1.1
11748  * Copyright(c) 2006-2007, Ext JS, LLC.
11749  *
11750  * Originally Released Under LGPL - original licence link has changed is not relivant.
11751  *
11752  * Fork - LGPL
11753  * <script type="text/javascript">
11754  */
11755
11756 /**
11757  * @class Roo.Toolbar
11758  * Basic Toolbar class.
11759  * @constructor
11760  * Creates a new Toolbar
11761  * @param {Object} container The config object
11762  */ 
11763 Roo.Toolbar = function(container, buttons, config)
11764 {
11765     /// old consturctor format still supported..
11766     if(container instanceof Array){ // omit the container for later rendering
11767         buttons = container;
11768         config = buttons;
11769         container = null;
11770     }
11771     if (typeof(container) == 'object' && container.xtype) {
11772         config = container;
11773         container = config.container;
11774         buttons = config.buttons || []; // not really - use items!!
11775     }
11776     var xitems = [];
11777     if (config && config.items) {
11778         xitems = config.items;
11779         delete config.items;
11780     }
11781     Roo.apply(this, config);
11782     this.buttons = buttons;
11783     
11784     if(container){
11785         this.render(container);
11786     }
11787     this.xitems = xitems;
11788     Roo.each(xitems, function(b) {
11789         this.add(b);
11790     }, this);
11791     
11792 };
11793
11794 Roo.Toolbar.prototype = {
11795     /**
11796      * @cfg {Array} items
11797      * array of button configs or elements to add (will be converted to a MixedCollection)
11798      */
11799     
11800     /**
11801      * @cfg {String/HTMLElement/Element} container
11802      * The id or element that will contain the toolbar
11803      */
11804     // private
11805     render : function(ct){
11806         this.el = Roo.get(ct);
11807         if(this.cls){
11808             this.el.addClass(this.cls);
11809         }
11810         // using a table allows for vertical alignment
11811         // 100% width is needed by Safari...
11812         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11813         this.tr = this.el.child("tr", true);
11814         var autoId = 0;
11815         this.items = new Roo.util.MixedCollection(false, function(o){
11816             return o.id || ("item" + (++autoId));
11817         });
11818         if(this.buttons){
11819             this.add.apply(this, this.buttons);
11820             delete this.buttons;
11821         }
11822     },
11823
11824     /**
11825      * Adds element(s) to the toolbar -- this function takes a variable number of 
11826      * arguments of mixed type and adds them to the toolbar.
11827      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11828      * <ul>
11829      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11830      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11831      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11832      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11833      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11834      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11835      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11836      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11837      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11838      * </ul>
11839      * @param {Mixed} arg2
11840      * @param {Mixed} etc.
11841      */
11842     add : function(){
11843         var a = arguments, l = a.length;
11844         for(var i = 0; i < l; i++){
11845             this._add(a[i]);
11846         }
11847     },
11848     // private..
11849     _add : function(el) {
11850         
11851         if (el.xtype) {
11852             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11853         }
11854         
11855         if (el.applyTo){ // some kind of form field
11856             return this.addField(el);
11857         } 
11858         if (el.render){ // some kind of Toolbar.Item
11859             return this.addItem(el);
11860         }
11861         if (typeof el == "string"){ // string
11862             if(el == "separator" || el == "-"){
11863                 return this.addSeparator();
11864             }
11865             if (el == " "){
11866                 return this.addSpacer();
11867             }
11868             if(el == "->"){
11869                 return this.addFill();
11870             }
11871             return this.addText(el);
11872             
11873         }
11874         if(el.tagName){ // element
11875             return this.addElement(el);
11876         }
11877         if(typeof el == "object"){ // must be button config?
11878             return this.addButton(el);
11879         }
11880         // and now what?!?!
11881         return false;
11882         
11883     },
11884     
11885     /**
11886      * Add an Xtype element
11887      * @param {Object} xtype Xtype Object
11888      * @return {Object} created Object
11889      */
11890     addxtype : function(e){
11891         return this.add(e);  
11892     },
11893     
11894     /**
11895      * Returns the Element for this toolbar.
11896      * @return {Roo.Element}
11897      */
11898     getEl : function(){
11899         return this.el;  
11900     },
11901     
11902     /**
11903      * Adds a separator
11904      * @return {Roo.Toolbar.Item} The separator item
11905      */
11906     addSeparator : function(){
11907         return this.addItem(new Roo.Toolbar.Separator());
11908     },
11909
11910     /**
11911      * Adds a spacer element
11912      * @return {Roo.Toolbar.Spacer} The spacer item
11913      */
11914     addSpacer : function(){
11915         return this.addItem(new Roo.Toolbar.Spacer());
11916     },
11917
11918     /**
11919      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11920      * @return {Roo.Toolbar.Fill} The fill item
11921      */
11922     addFill : function(){
11923         return this.addItem(new Roo.Toolbar.Fill());
11924     },
11925
11926     /**
11927      * Adds any standard HTML element to the toolbar
11928      * @param {String/HTMLElement/Element} el The element or id of the element to add
11929      * @return {Roo.Toolbar.Item} The element's item
11930      */
11931     addElement : function(el){
11932         return this.addItem(new Roo.Toolbar.Item(el));
11933     },
11934     /**
11935      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11936      * @type Roo.util.MixedCollection  
11937      */
11938     items : false,
11939      
11940     /**
11941      * Adds any Toolbar.Item or subclass
11942      * @param {Roo.Toolbar.Item} item
11943      * @return {Roo.Toolbar.Item} The item
11944      */
11945     addItem : function(item){
11946         var td = this.nextBlock();
11947         item.render(td);
11948         this.items.add(item);
11949         return item;
11950     },
11951     
11952     /**
11953      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11954      * @param {Object/Array} config A button config or array of configs
11955      * @return {Roo.Toolbar.Button/Array}
11956      */
11957     addButton : function(config){
11958         if(config instanceof Array){
11959             var buttons = [];
11960             for(var i = 0, len = config.length; i < len; i++) {
11961                 buttons.push(this.addButton(config[i]));
11962             }
11963             return buttons;
11964         }
11965         var b = config;
11966         if(!(config instanceof Roo.Toolbar.Button)){
11967             b = config.split ?
11968                 new Roo.Toolbar.SplitButton(config) :
11969                 new Roo.Toolbar.Button(config);
11970         }
11971         var td = this.nextBlock();
11972         b.render(td);
11973         this.items.add(b);
11974         return b;
11975     },
11976     
11977     /**
11978      * Adds text to the toolbar
11979      * @param {String} text The text to add
11980      * @return {Roo.Toolbar.Item} The element's item
11981      */
11982     addText : function(text){
11983         return this.addItem(new Roo.Toolbar.TextItem(text));
11984     },
11985     
11986     /**
11987      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11988      * @param {Number} index The index where the item is to be inserted
11989      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11990      * @return {Roo.Toolbar.Button/Item}
11991      */
11992     insertButton : function(index, item){
11993         if(item instanceof Array){
11994             var buttons = [];
11995             for(var i = 0, len = item.length; i < len; i++) {
11996                buttons.push(this.insertButton(index + i, item[i]));
11997             }
11998             return buttons;
11999         }
12000         if (!(item instanceof Roo.Toolbar.Button)){
12001            item = new Roo.Toolbar.Button(item);
12002         }
12003         var td = document.createElement("td");
12004         this.tr.insertBefore(td, this.tr.childNodes[index]);
12005         item.render(td);
12006         this.items.insert(index, item);
12007         return item;
12008     },
12009     
12010     /**
12011      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12012      * @param {Object} config
12013      * @return {Roo.Toolbar.Item} The element's item
12014      */
12015     addDom : function(config, returnEl){
12016         var td = this.nextBlock();
12017         Roo.DomHelper.overwrite(td, config);
12018         var ti = new Roo.Toolbar.Item(td.firstChild);
12019         ti.render(td);
12020         this.items.add(ti);
12021         return ti;
12022     },
12023
12024     /**
12025      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12026      * @type Roo.util.MixedCollection  
12027      */
12028     fields : false,
12029     
12030     /**
12031      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12032      * Note: the field should not have been rendered yet. For a field that has already been
12033      * rendered, use {@link #addElement}.
12034      * @param {Roo.form.Field} field
12035      * @return {Roo.ToolbarItem}
12036      */
12037      
12038       
12039     addField : function(field) {
12040         if (!this.fields) {
12041             var autoId = 0;
12042             this.fields = new Roo.util.MixedCollection(false, function(o){
12043                 return o.id || ("item" + (++autoId));
12044             });
12045
12046         }
12047         
12048         var td = this.nextBlock();
12049         field.render(td);
12050         var ti = new Roo.Toolbar.Item(td.firstChild);
12051         ti.render(td);
12052         this.items.add(ti);
12053         this.fields.add(field);
12054         return ti;
12055     },
12056     /**
12057      * Hide the toolbar
12058      * @method hide
12059      */
12060      
12061       
12062     hide : function()
12063     {
12064         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12065         this.el.child('div').hide();
12066     },
12067     /**
12068      * Show the toolbar
12069      * @method show
12070      */
12071     show : function()
12072     {
12073         this.el.child('div').show();
12074     },
12075       
12076     // private
12077     nextBlock : function(){
12078         var td = document.createElement("td");
12079         this.tr.appendChild(td);
12080         return td;
12081     },
12082
12083     // private
12084     destroy : function(){
12085         if(this.items){ // rendered?
12086             Roo.destroy.apply(Roo, this.items.items);
12087         }
12088         if(this.fields){ // rendered?
12089             Roo.destroy.apply(Roo, this.fields.items);
12090         }
12091         Roo.Element.uncache(this.el, this.tr);
12092     }
12093 };
12094
12095 /**
12096  * @class Roo.Toolbar.Item
12097  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12098  * @constructor
12099  * Creates a new Item
12100  * @param {HTMLElement} el 
12101  */
12102 Roo.Toolbar.Item = function(el){
12103     var cfg = {};
12104     if (typeof (el.xtype) != 'undefined') {
12105         cfg = el;
12106         el = cfg.el;
12107     }
12108     
12109     this.el = Roo.getDom(el);
12110     this.id = Roo.id(this.el);
12111     this.hidden = false;
12112     
12113     this.addEvents({
12114          /**
12115              * @event render
12116              * Fires when the button is rendered
12117              * @param {Button} this
12118              */
12119         'render': true
12120     });
12121     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12122 };
12123 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12124 //Roo.Toolbar.Item.prototype = {
12125     
12126     /**
12127      * Get this item's HTML Element
12128      * @return {HTMLElement}
12129      */
12130     getEl : function(){
12131        return this.el;  
12132     },
12133
12134     // private
12135     render : function(td){
12136         
12137          this.td = td;
12138         td.appendChild(this.el);
12139         
12140         this.fireEvent('render', this);
12141     },
12142     
12143     /**
12144      * Removes and destroys this item.
12145      */
12146     destroy : function(){
12147         this.td.parentNode.removeChild(this.td);
12148     },
12149     
12150     /**
12151      * Shows this item.
12152      */
12153     show: function(){
12154         this.hidden = false;
12155         this.td.style.display = "";
12156     },
12157     
12158     /**
12159      * Hides this item.
12160      */
12161     hide: function(){
12162         this.hidden = true;
12163         this.td.style.display = "none";
12164     },
12165     
12166     /**
12167      * Convenience function for boolean show/hide.
12168      * @param {Boolean} visible true to show/false to hide
12169      */
12170     setVisible: function(visible){
12171         if(visible) {
12172             this.show();
12173         }else{
12174             this.hide();
12175         }
12176     },
12177     
12178     /**
12179      * Try to focus this item.
12180      */
12181     focus : function(){
12182         Roo.fly(this.el).focus();
12183     },
12184     
12185     /**
12186      * Disables this item.
12187      */
12188     disable : function(){
12189         Roo.fly(this.td).addClass("x-item-disabled");
12190         this.disabled = true;
12191         this.el.disabled = true;
12192     },
12193     
12194     /**
12195      * Enables this item.
12196      */
12197     enable : function(){
12198         Roo.fly(this.td).removeClass("x-item-disabled");
12199         this.disabled = false;
12200         this.el.disabled = false;
12201     }
12202 });
12203
12204
12205 /**
12206  * @class Roo.Toolbar.Separator
12207  * @extends Roo.Toolbar.Item
12208  * A simple toolbar separator class
12209  * @constructor
12210  * Creates a new Separator
12211  */
12212 Roo.Toolbar.Separator = function(cfg){
12213     
12214     var s = document.createElement("span");
12215     s.className = "ytb-sep";
12216     if (cfg) {
12217         cfg.el = s;
12218     }
12219     
12220     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12221 };
12222 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12223     enable:Roo.emptyFn,
12224     disable:Roo.emptyFn,
12225     focus:Roo.emptyFn
12226 });
12227
12228 /**
12229  * @class Roo.Toolbar.Spacer
12230  * @extends Roo.Toolbar.Item
12231  * A simple element that adds extra horizontal space to a toolbar.
12232  * @constructor
12233  * Creates a new Spacer
12234  */
12235 Roo.Toolbar.Spacer = function(cfg){
12236     var s = document.createElement("div");
12237     s.className = "ytb-spacer";
12238     if (cfg) {
12239         cfg.el = s;
12240     }
12241     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12242 };
12243 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12244     enable:Roo.emptyFn,
12245     disable:Roo.emptyFn,
12246     focus:Roo.emptyFn
12247 });
12248
12249 /**
12250  * @class Roo.Toolbar.Fill
12251  * @extends Roo.Toolbar.Spacer
12252  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12253  * @constructor
12254  * Creates a new Spacer
12255  */
12256 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12257     // private
12258     render : function(td){
12259         td.style.width = '100%';
12260         Roo.Toolbar.Fill.superclass.render.call(this, td);
12261     }
12262 });
12263
12264 /**
12265  * @class Roo.Toolbar.TextItem
12266  * @extends Roo.Toolbar.Item
12267  * A simple class that renders text directly into a toolbar.
12268  * @constructor
12269  * Creates a new TextItem
12270  * @param {String} text
12271  */
12272 Roo.Toolbar.TextItem = function(cfg){
12273     var  text = cfg || "";
12274     if (typeof(cfg) == 'object') {
12275         text = cfg.text || "";
12276     }  else {
12277         cfg = null;
12278     }
12279     var s = document.createElement("span");
12280     s.className = "ytb-text";
12281     s.innerHTML = text;
12282     if (cfg) {
12283         cfg.el  = s;
12284     }
12285     
12286     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12287 };
12288 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12289     
12290      
12291     enable:Roo.emptyFn,
12292     disable:Roo.emptyFn,
12293     focus:Roo.emptyFn
12294 });
12295
12296 /**
12297  * @class Roo.Toolbar.Button
12298  * @extends Roo.Button
12299  * A button that renders into a toolbar.
12300  * @constructor
12301  * Creates a new Button
12302  * @param {Object} config A standard {@link Roo.Button} config object
12303  */
12304 Roo.Toolbar.Button = function(config){
12305     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12306 };
12307 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12308     render : function(td){
12309         this.td = td;
12310         Roo.Toolbar.Button.superclass.render.call(this, td);
12311     },
12312     
12313     /**
12314      * Removes and destroys this button
12315      */
12316     destroy : function(){
12317         Roo.Toolbar.Button.superclass.destroy.call(this);
12318         this.td.parentNode.removeChild(this.td);
12319     },
12320     
12321     /**
12322      * Shows this button
12323      */
12324     show: function(){
12325         this.hidden = false;
12326         this.td.style.display = "";
12327     },
12328     
12329     /**
12330      * Hides this button
12331      */
12332     hide: function(){
12333         this.hidden = true;
12334         this.td.style.display = "none";
12335     },
12336
12337     /**
12338      * Disables this item
12339      */
12340     disable : function(){
12341         Roo.fly(this.td).addClass("x-item-disabled");
12342         this.disabled = true;
12343     },
12344
12345     /**
12346      * Enables this item
12347      */
12348     enable : function(){
12349         Roo.fly(this.td).removeClass("x-item-disabled");
12350         this.disabled = false;
12351     }
12352 });
12353 // backwards compat
12354 Roo.ToolbarButton = Roo.Toolbar.Button;
12355
12356 /**
12357  * @class Roo.Toolbar.SplitButton
12358  * @extends Roo.SplitButton
12359  * A menu button that renders into a toolbar.
12360  * @constructor
12361  * Creates a new SplitButton
12362  * @param {Object} config A standard {@link Roo.SplitButton} config object
12363  */
12364 Roo.Toolbar.SplitButton = function(config){
12365     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12366 };
12367 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12368     render : function(td){
12369         this.td = td;
12370         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12371     },
12372     
12373     /**
12374      * Removes and destroys this button
12375      */
12376     destroy : function(){
12377         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12378         this.td.parentNode.removeChild(this.td);
12379     },
12380     
12381     /**
12382      * Shows this button
12383      */
12384     show: function(){
12385         this.hidden = false;
12386         this.td.style.display = "";
12387     },
12388     
12389     /**
12390      * Hides this button
12391      */
12392     hide: function(){
12393         this.hidden = true;
12394         this.td.style.display = "none";
12395     }
12396 });
12397
12398 // backwards compat
12399 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12400  * Based on:
12401  * Ext JS Library 1.1.1
12402  * Copyright(c) 2006-2007, Ext JS, LLC.
12403  *
12404  * Originally Released Under LGPL - original licence link has changed is not relivant.
12405  *
12406  * Fork - LGPL
12407  * <script type="text/javascript">
12408  */
12409  
12410 /**
12411  * @class Roo.PagingToolbar
12412  * @extends Roo.Toolbar
12413  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12414  * @constructor
12415  * Create a new PagingToolbar
12416  * @param {Object} config The config object
12417  */
12418 Roo.PagingToolbar = function(el, ds, config)
12419 {
12420     // old args format still supported... - xtype is prefered..
12421     if (typeof(el) == 'object' && el.xtype) {
12422         // created from xtype...
12423         config = el;
12424         ds = el.dataSource;
12425         el = config.container;
12426     }
12427     var items = [];
12428     if (config.items) {
12429         items = config.items;
12430         config.items = [];
12431     }
12432     
12433     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12434     this.ds = ds;
12435     this.cursor = 0;
12436     this.renderButtons(this.el);
12437     this.bind(ds);
12438     
12439     // supprot items array.
12440    
12441     Roo.each(items, function(e) {
12442         this.add(Roo.factory(e));
12443     },this);
12444     
12445 };
12446
12447 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12448     /**
12449      * @cfg {Roo.data.Store} dataSource
12450      * The underlying data store providing the paged data
12451      */
12452     /**
12453      * @cfg {String/HTMLElement/Element} container
12454      * container The id or element that will contain the toolbar
12455      */
12456     /**
12457      * @cfg {Boolean} displayInfo
12458      * True to display the displayMsg (defaults to false)
12459      */
12460     /**
12461      * @cfg {Number} pageSize
12462      * The number of records to display per page (defaults to 20)
12463      */
12464     pageSize: 20,
12465     /**
12466      * @cfg {String} displayMsg
12467      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12468      */
12469     displayMsg : 'Displaying {0} - {1} of {2}',
12470     /**
12471      * @cfg {String} emptyMsg
12472      * The message to display when no records are found (defaults to "No data to display")
12473      */
12474     emptyMsg : 'No data to display',
12475     /**
12476      * Customizable piece of the default paging text (defaults to "Page")
12477      * @type String
12478      */
12479     beforePageText : "Page",
12480     /**
12481      * Customizable piece of the default paging text (defaults to "of %0")
12482      * @type String
12483      */
12484     afterPageText : "of {0}",
12485     /**
12486      * Customizable piece of the default paging text (defaults to "First Page")
12487      * @type String
12488      */
12489     firstText : "First Page",
12490     /**
12491      * Customizable piece of the default paging text (defaults to "Previous Page")
12492      * @type String
12493      */
12494     prevText : "Previous Page",
12495     /**
12496      * Customizable piece of the default paging text (defaults to "Next Page")
12497      * @type String
12498      */
12499     nextText : "Next Page",
12500     /**
12501      * Customizable piece of the default paging text (defaults to "Last Page")
12502      * @type String
12503      */
12504     lastText : "Last Page",
12505     /**
12506      * Customizable piece of the default paging text (defaults to "Refresh")
12507      * @type String
12508      */
12509     refreshText : "Refresh",
12510
12511     // private
12512     renderButtons : function(el){
12513         Roo.PagingToolbar.superclass.render.call(this, el);
12514         this.first = this.addButton({
12515             tooltip: this.firstText,
12516             cls: "x-btn-icon x-grid-page-first",
12517             disabled: true,
12518             handler: this.onClick.createDelegate(this, ["first"])
12519         });
12520         this.prev = this.addButton({
12521             tooltip: this.prevText,
12522             cls: "x-btn-icon x-grid-page-prev",
12523             disabled: true,
12524             handler: this.onClick.createDelegate(this, ["prev"])
12525         });
12526         //this.addSeparator();
12527         this.add(this.beforePageText);
12528         this.field = Roo.get(this.addDom({
12529            tag: "input",
12530            type: "text",
12531            size: "3",
12532            value: "1",
12533            cls: "x-grid-page-number"
12534         }).el);
12535         this.field.on("keydown", this.onPagingKeydown, this);
12536         this.field.on("focus", function(){this.dom.select();});
12537         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12538         this.field.setHeight(18);
12539         //this.addSeparator();
12540         this.next = this.addButton({
12541             tooltip: this.nextText,
12542             cls: "x-btn-icon x-grid-page-next",
12543             disabled: true,
12544             handler: this.onClick.createDelegate(this, ["next"])
12545         });
12546         this.last = this.addButton({
12547             tooltip: this.lastText,
12548             cls: "x-btn-icon x-grid-page-last",
12549             disabled: true,
12550             handler: this.onClick.createDelegate(this, ["last"])
12551         });
12552         //this.addSeparator();
12553         this.loading = this.addButton({
12554             tooltip: this.refreshText,
12555             cls: "x-btn-icon x-grid-loading",
12556             handler: this.onClick.createDelegate(this, ["refresh"])
12557         });
12558
12559         if(this.displayInfo){
12560             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12561         }
12562     },
12563
12564     // private
12565     updateInfo : function(){
12566         if(this.displayEl){
12567             var count = this.ds.getCount();
12568             var msg = count == 0 ?
12569                 this.emptyMsg :
12570                 String.format(
12571                     this.displayMsg,
12572                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12573                 );
12574             this.displayEl.update(msg);
12575         }
12576     },
12577
12578     // private
12579     onLoad : function(ds, r, o){
12580        this.cursor = o.params ? o.params.start : 0;
12581        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12582
12583        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12584        this.field.dom.value = ap;
12585        this.first.setDisabled(ap == 1);
12586        this.prev.setDisabled(ap == 1);
12587        this.next.setDisabled(ap == ps);
12588        this.last.setDisabled(ap == ps);
12589        this.loading.enable();
12590        this.updateInfo();
12591     },
12592
12593     // private
12594     getPageData : function(){
12595         var total = this.ds.getTotalCount();
12596         return {
12597             total : total,
12598             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12599             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12600         };
12601     },
12602
12603     // private
12604     onLoadError : function(){
12605         this.loading.enable();
12606     },
12607
12608     // private
12609     onPagingKeydown : function(e){
12610         var k = e.getKey();
12611         var d = this.getPageData();
12612         if(k == e.RETURN){
12613             var v = this.field.dom.value, pageNum;
12614             if(!v || isNaN(pageNum = parseInt(v, 10))){
12615                 this.field.dom.value = d.activePage;
12616                 return;
12617             }
12618             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12619             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12620             e.stopEvent();
12621         }
12622         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
12623         {
12624           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12625           this.field.dom.value = pageNum;
12626           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12627           e.stopEvent();
12628         }
12629         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12630         {
12631           var v = this.field.dom.value, pageNum; 
12632           var increment = (e.shiftKey) ? 10 : 1;
12633           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
12634             increment *= -1;
12635           }
12636           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12637             this.field.dom.value = d.activePage;
12638             return;
12639           }
12640           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12641           {
12642             this.field.dom.value = parseInt(v, 10) + increment;
12643             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12644             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12645           }
12646           e.stopEvent();
12647         }
12648     },
12649
12650     // private
12651     beforeLoad : function(){
12652         if(this.loading){
12653             this.loading.disable();
12654         }
12655     },
12656
12657     // private
12658     onClick : function(which){
12659         var ds = this.ds;
12660         switch(which){
12661             case "first":
12662                 ds.load({params:{start: 0, limit: this.pageSize}});
12663             break;
12664             case "prev":
12665                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12666             break;
12667             case "next":
12668                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12669             break;
12670             case "last":
12671                 var total = ds.getTotalCount();
12672                 var extra = total % this.pageSize;
12673                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12674                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12675             break;
12676             case "refresh":
12677                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12678             break;
12679         }
12680     },
12681
12682     /**
12683      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12684      * @param {Roo.data.Store} store The data store to unbind
12685      */
12686     unbind : function(ds){
12687         ds.un("beforeload", this.beforeLoad, this);
12688         ds.un("load", this.onLoad, this);
12689         ds.un("loadexception", this.onLoadError, this);
12690         ds.un("remove", this.updateInfo, this);
12691         ds.un("add", this.updateInfo, this);
12692         this.ds = undefined;
12693     },
12694
12695     /**
12696      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12697      * @param {Roo.data.Store} store The data store to bind
12698      */
12699     bind : function(ds){
12700         ds.on("beforeload", this.beforeLoad, this);
12701         ds.on("load", this.onLoad, this);
12702         ds.on("loadexception", this.onLoadError, this);
12703         ds.on("remove", this.updateInfo, this);
12704         ds.on("add", this.updateInfo, this);
12705         this.ds = ds;
12706     }
12707 });/*
12708  * Based on:
12709  * Ext JS Library 1.1.1
12710  * Copyright(c) 2006-2007, Ext JS, LLC.
12711  *
12712  * Originally Released Under LGPL - original licence link has changed is not relivant.
12713  *
12714  * Fork - LGPL
12715  * <script type="text/javascript">
12716  */
12717
12718 /**
12719  * @class Roo.Resizable
12720  * @extends Roo.util.Observable
12721  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12722  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12723  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
12724  * the element will be wrapped for you automatically.</p>
12725  * <p>Here is the list of valid resize handles:</p>
12726  * <pre>
12727 Value   Description
12728 ------  -------------------
12729  'n'     north
12730  's'     south
12731  'e'     east
12732  'w'     west
12733  'nw'    northwest
12734  'sw'    southwest
12735  'se'    southeast
12736  'ne'    northeast
12737  'hd'    horizontal drag
12738  'all'   all
12739 </pre>
12740  * <p>Here's an example showing the creation of a typical Resizable:</p>
12741  * <pre><code>
12742 var resizer = new Roo.Resizable("element-id", {
12743     handles: 'all',
12744     minWidth: 200,
12745     minHeight: 100,
12746     maxWidth: 500,
12747     maxHeight: 400,
12748     pinned: true
12749 });
12750 resizer.on("resize", myHandler);
12751 </code></pre>
12752  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12753  * resizer.east.setDisplayed(false);</p>
12754  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12755  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12756  * resize operation's new size (defaults to [0, 0])
12757  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12758  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12759  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12760  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12761  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12762  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12763  * @cfg {Number} width The width of the element in pixels (defaults to null)
12764  * @cfg {Number} height The height of the element in pixels (defaults to null)
12765  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12766  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12767  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12768  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12769  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12770  * in favor of the handles config option (defaults to false)
12771  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12772  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12773  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12774  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12775  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12776  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12777  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12778  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12779  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12780  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12781  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12782  * @constructor
12783  * Create a new resizable component
12784  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12785  * @param {Object} config configuration options
12786   */
12787 Roo.Resizable = function(el, config)
12788 {
12789     this.el = Roo.get(el);
12790
12791     if(config && config.wrap){
12792         config.resizeChild = this.el;
12793         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12794         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12795         this.el.setStyle("overflow", "hidden");
12796         this.el.setPositioning(config.resizeChild.getPositioning());
12797         config.resizeChild.clearPositioning();
12798         if(!config.width || !config.height){
12799             var csize = config.resizeChild.getSize();
12800             this.el.setSize(csize.width, csize.height);
12801         }
12802         if(config.pinned && !config.adjustments){
12803             config.adjustments = "auto";
12804         }
12805     }
12806
12807     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12808     this.proxy.unselectable();
12809     this.proxy.enableDisplayMode('block');
12810
12811     Roo.apply(this, config);
12812
12813     if(this.pinned){
12814         this.disableTrackOver = true;
12815         this.el.addClass("x-resizable-pinned");
12816     }
12817     // if the element isn't positioned, make it relative
12818     var position = this.el.getStyle("position");
12819     if(position != "absolute" && position != "fixed"){
12820         this.el.setStyle("position", "relative");
12821     }
12822     if(!this.handles){ // no handles passed, must be legacy style
12823         this.handles = 's,e,se';
12824         if(this.multiDirectional){
12825             this.handles += ',n,w';
12826         }
12827     }
12828     if(this.handles == "all"){
12829         this.handles = "n s e w ne nw se sw";
12830     }
12831     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12832     var ps = Roo.Resizable.positions;
12833     for(var i = 0, len = hs.length; i < len; i++){
12834         if(hs[i] && ps[hs[i]]){
12835             var pos = ps[hs[i]];
12836             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12837         }
12838     }
12839     // legacy
12840     this.corner = this.southeast;
12841     
12842     // updateBox = the box can move..
12843     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12844         this.updateBox = true;
12845     }
12846
12847     this.activeHandle = null;
12848
12849     if(this.resizeChild){
12850         if(typeof this.resizeChild == "boolean"){
12851             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12852         }else{
12853             this.resizeChild = Roo.get(this.resizeChild, true);
12854         }
12855     }
12856     
12857     if(this.adjustments == "auto"){
12858         var rc = this.resizeChild;
12859         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12860         if(rc && (hw || hn)){
12861             rc.position("relative");
12862             rc.setLeft(hw ? hw.el.getWidth() : 0);
12863             rc.setTop(hn ? hn.el.getHeight() : 0);
12864         }
12865         this.adjustments = [
12866             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12867             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12868         ];
12869     }
12870
12871     if(this.draggable){
12872         this.dd = this.dynamic ?
12873             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12874         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12875     }
12876
12877     // public events
12878     this.addEvents({
12879         /**
12880          * @event beforeresize
12881          * Fired before resize is allowed. Set enabled to false to cancel resize.
12882          * @param {Roo.Resizable} this
12883          * @param {Roo.EventObject} e The mousedown event
12884          */
12885         "beforeresize" : true,
12886         /**
12887          * @event resizing
12888          * Fired a resizing.
12889          * @param {Roo.Resizable} this
12890          * @param {Number} x The new x position
12891          * @param {Number} y The new y position
12892          * @param {Number} w The new w width
12893          * @param {Number} h The new h hight
12894          * @param {Roo.EventObject} e The mouseup event
12895          */
12896         "resizing" : true,
12897         /**
12898          * @event resize
12899          * Fired after a resize.
12900          * @param {Roo.Resizable} this
12901          * @param {Number} width The new width
12902          * @param {Number} height The new height
12903          * @param {Roo.EventObject} e The mouseup event
12904          */
12905         "resize" : true
12906     });
12907
12908     if(this.width !== null && this.height !== null){
12909         this.resizeTo(this.width, this.height);
12910     }else{
12911         this.updateChildSize();
12912     }
12913     if(Roo.isIE){
12914         this.el.dom.style.zoom = 1;
12915     }
12916     Roo.Resizable.superclass.constructor.call(this);
12917 };
12918
12919 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12920         resizeChild : false,
12921         adjustments : [0, 0],
12922         minWidth : 5,
12923         minHeight : 5,
12924         maxWidth : 10000,
12925         maxHeight : 10000,
12926         enabled : true,
12927         animate : false,
12928         duration : .35,
12929         dynamic : false,
12930         handles : false,
12931         multiDirectional : false,
12932         disableTrackOver : false,
12933         easing : 'easeOutStrong',
12934         widthIncrement : 0,
12935         heightIncrement : 0,
12936         pinned : false,
12937         width : null,
12938         height : null,
12939         preserveRatio : false,
12940         transparent: false,
12941         minX: 0,
12942         minY: 0,
12943         draggable: false,
12944
12945         /**
12946          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12947          */
12948         constrainTo: undefined,
12949         /**
12950          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12951          */
12952         resizeRegion: undefined,
12953
12954
12955     /**
12956      * Perform a manual resize
12957      * @param {Number} width
12958      * @param {Number} height
12959      */
12960     resizeTo : function(width, height){
12961         this.el.setSize(width, height);
12962         this.updateChildSize();
12963         this.fireEvent("resize", this, width, height, null);
12964     },
12965
12966     // private
12967     startSizing : function(e, handle){
12968         this.fireEvent("beforeresize", this, e);
12969         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12970
12971             if(!this.overlay){
12972                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12973                 this.overlay.unselectable();
12974                 this.overlay.enableDisplayMode("block");
12975                 this.overlay.on("mousemove", this.onMouseMove, this);
12976                 this.overlay.on("mouseup", this.onMouseUp, this);
12977             }
12978             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12979
12980             this.resizing = true;
12981             this.startBox = this.el.getBox();
12982             this.startPoint = e.getXY();
12983             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12984                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12985
12986             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12987             this.overlay.show();
12988
12989             if(this.constrainTo) {
12990                 var ct = Roo.get(this.constrainTo);
12991                 this.resizeRegion = ct.getRegion().adjust(
12992                     ct.getFrameWidth('t'),
12993                     ct.getFrameWidth('l'),
12994                     -ct.getFrameWidth('b'),
12995                     -ct.getFrameWidth('r')
12996                 );
12997             }
12998
12999             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13000             this.proxy.show();
13001             this.proxy.setBox(this.startBox);
13002             if(!this.dynamic){
13003                 this.proxy.setStyle('visibility', 'visible');
13004             }
13005         }
13006     },
13007
13008     // private
13009     onMouseDown : function(handle, e){
13010         if(this.enabled){
13011             e.stopEvent();
13012             this.activeHandle = handle;
13013             this.startSizing(e, handle);
13014         }
13015     },
13016
13017     // private
13018     onMouseUp : function(e){
13019         var size = this.resizeElement();
13020         this.resizing = false;
13021         this.handleOut();
13022         this.overlay.hide();
13023         this.proxy.hide();
13024         this.fireEvent("resize", this, size.width, size.height, e);
13025     },
13026
13027     // private
13028     updateChildSize : function(){
13029         
13030         if(this.resizeChild){
13031             var el = this.el;
13032             var child = this.resizeChild;
13033             var adj = this.adjustments;
13034             if(el.dom.offsetWidth){
13035                 var b = el.getSize(true);
13036                 child.setSize(b.width+adj[0], b.height+adj[1]);
13037             }
13038             // Second call here for IE
13039             // The first call enables instant resizing and
13040             // the second call corrects scroll bars if they
13041             // exist
13042             if(Roo.isIE){
13043                 setTimeout(function(){
13044                     if(el.dom.offsetWidth){
13045                         var b = el.getSize(true);
13046                         child.setSize(b.width+adj[0], b.height+adj[1]);
13047                     }
13048                 }, 10);
13049             }
13050         }
13051     },
13052
13053     // private
13054     snap : function(value, inc, min){
13055         if(!inc || !value) {
13056             return value;
13057         }
13058         var newValue = value;
13059         var m = value % inc;
13060         if(m > 0){
13061             if(m > (inc/2)){
13062                 newValue = value + (inc-m);
13063             }else{
13064                 newValue = value - m;
13065             }
13066         }
13067         return Math.max(min, newValue);
13068     },
13069
13070     // private
13071     resizeElement : function(){
13072         var box = this.proxy.getBox();
13073         if(this.updateBox){
13074             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13075         }else{
13076             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13077         }
13078         this.updateChildSize();
13079         if(!this.dynamic){
13080             this.proxy.hide();
13081         }
13082         return box;
13083     },
13084
13085     // private
13086     constrain : function(v, diff, m, mx){
13087         if(v - diff < m){
13088             diff = v - m;
13089         }else if(v - diff > mx){
13090             diff = mx - v;
13091         }
13092         return diff;
13093     },
13094
13095     // private
13096     onMouseMove : function(e){
13097         
13098         if(this.enabled){
13099             try{// try catch so if something goes wrong the user doesn't get hung
13100
13101             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13102                 return;
13103             }
13104
13105             //var curXY = this.startPoint;
13106             var curSize = this.curSize || this.startBox;
13107             var x = this.startBox.x, y = this.startBox.y;
13108             var ox = x, oy = y;
13109             var w = curSize.width, h = curSize.height;
13110             var ow = w, oh = h;
13111             var mw = this.minWidth, mh = this.minHeight;
13112             var mxw = this.maxWidth, mxh = this.maxHeight;
13113             var wi = this.widthIncrement;
13114             var hi = this.heightIncrement;
13115
13116             var eventXY = e.getXY();
13117             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13118             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13119
13120             var pos = this.activeHandle.position;
13121
13122             switch(pos){
13123                 case "east":
13124                     w += diffX;
13125                     w = Math.min(Math.max(mw, w), mxw);
13126                     break;
13127              
13128                 case "south":
13129                     h += diffY;
13130                     h = Math.min(Math.max(mh, h), mxh);
13131                     break;
13132                 case "southeast":
13133                     w += diffX;
13134                     h += diffY;
13135                     w = Math.min(Math.max(mw, w), mxw);
13136                     h = Math.min(Math.max(mh, h), mxh);
13137                     break;
13138                 case "north":
13139                     diffY = this.constrain(h, diffY, mh, mxh);
13140                     y += diffY;
13141                     h -= diffY;
13142                     break;
13143                 case "hdrag":
13144                     
13145                     if (wi) {
13146                         var adiffX = Math.abs(diffX);
13147                         var sub = (adiffX % wi); // how much 
13148                         if (sub > (wi/2)) { // far enough to snap
13149                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13150                         } else {
13151                             // remove difference.. 
13152                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13153                         }
13154                     }
13155                     x += diffX;
13156                     x = Math.max(this.minX, x);
13157                     break;
13158                 case "west":
13159                     diffX = this.constrain(w, diffX, mw, mxw);
13160                     x += diffX;
13161                     w -= diffX;
13162                     break;
13163                 case "northeast":
13164                     w += diffX;
13165                     w = Math.min(Math.max(mw, w), mxw);
13166                     diffY = this.constrain(h, diffY, mh, mxh);
13167                     y += diffY;
13168                     h -= diffY;
13169                     break;
13170                 case "northwest":
13171                     diffX = this.constrain(w, diffX, mw, mxw);
13172                     diffY = this.constrain(h, diffY, mh, mxh);
13173                     y += diffY;
13174                     h -= diffY;
13175                     x += diffX;
13176                     w -= diffX;
13177                     break;
13178                case "southwest":
13179                     diffX = this.constrain(w, diffX, mw, mxw);
13180                     h += diffY;
13181                     h = Math.min(Math.max(mh, h), mxh);
13182                     x += diffX;
13183                     w -= diffX;
13184                     break;
13185             }
13186
13187             var sw = this.snap(w, wi, mw);
13188             var sh = this.snap(h, hi, mh);
13189             if(sw != w || sh != h){
13190                 switch(pos){
13191                     case "northeast":
13192                         y -= sh - h;
13193                     break;
13194                     case "north":
13195                         y -= sh - h;
13196                         break;
13197                     case "southwest":
13198                         x -= sw - w;
13199                     break;
13200                     case "west":
13201                         x -= sw - w;
13202                         break;
13203                     case "northwest":
13204                         x -= sw - w;
13205                         y -= sh - h;
13206                     break;
13207                 }
13208                 w = sw;
13209                 h = sh;
13210             }
13211
13212             if(this.preserveRatio){
13213                 switch(pos){
13214                     case "southeast":
13215                     case "east":
13216                         h = oh * (w/ow);
13217                         h = Math.min(Math.max(mh, h), mxh);
13218                         w = ow * (h/oh);
13219                        break;
13220                     case "south":
13221                         w = ow * (h/oh);
13222                         w = Math.min(Math.max(mw, w), mxw);
13223                         h = oh * (w/ow);
13224                         break;
13225                     case "northeast":
13226                         w = ow * (h/oh);
13227                         w = Math.min(Math.max(mw, w), mxw);
13228                         h = oh * (w/ow);
13229                     break;
13230                     case "north":
13231                         var tw = w;
13232                         w = ow * (h/oh);
13233                         w = Math.min(Math.max(mw, w), mxw);
13234                         h = oh * (w/ow);
13235                         x += (tw - w) / 2;
13236                         break;
13237                     case "southwest":
13238                         h = oh * (w/ow);
13239                         h = Math.min(Math.max(mh, h), mxh);
13240                         var tw = w;
13241                         w = ow * (h/oh);
13242                         x += tw - w;
13243                         break;
13244                     case "west":
13245                         var th = h;
13246                         h = oh * (w/ow);
13247                         h = Math.min(Math.max(mh, h), mxh);
13248                         y += (th - h) / 2;
13249                         var tw = w;
13250                         w = ow * (h/oh);
13251                         x += tw - w;
13252                        break;
13253                     case "northwest":
13254                         var tw = w;
13255                         var th = h;
13256                         h = oh * (w/ow);
13257                         h = Math.min(Math.max(mh, h), mxh);
13258                         w = ow * (h/oh);
13259                         y += th - h;
13260                         x += tw - w;
13261                        break;
13262
13263                 }
13264             }
13265             if (pos == 'hdrag') {
13266                 w = ow;
13267             }
13268             this.proxy.setBounds(x, y, w, h);
13269             if(this.dynamic){
13270                 this.resizeElement();
13271             }
13272             }catch(e){}
13273         }
13274         this.fireEvent("resizing", this, x, y, w, h, e);
13275     },
13276
13277     // private
13278     handleOver : function(){
13279         if(this.enabled){
13280             this.el.addClass("x-resizable-over");
13281         }
13282     },
13283
13284     // private
13285     handleOut : function(){
13286         if(!this.resizing){
13287             this.el.removeClass("x-resizable-over");
13288         }
13289     },
13290
13291     /**
13292      * Returns the element this component is bound to.
13293      * @return {Roo.Element}
13294      */
13295     getEl : function(){
13296         return this.el;
13297     },
13298
13299     /**
13300      * Returns the resizeChild element (or null).
13301      * @return {Roo.Element}
13302      */
13303     getResizeChild : function(){
13304         return this.resizeChild;
13305     },
13306     groupHandler : function()
13307     {
13308         
13309     },
13310     /**
13311      * Destroys this resizable. If the element was wrapped and
13312      * removeEl is not true then the element remains.
13313      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13314      */
13315     destroy : function(removeEl){
13316         this.proxy.remove();
13317         if(this.overlay){
13318             this.overlay.removeAllListeners();
13319             this.overlay.remove();
13320         }
13321         var ps = Roo.Resizable.positions;
13322         for(var k in ps){
13323             if(typeof ps[k] != "function" && this[ps[k]]){
13324                 var h = this[ps[k]];
13325                 h.el.removeAllListeners();
13326                 h.el.remove();
13327             }
13328         }
13329         if(removeEl){
13330             this.el.update("");
13331             this.el.remove();
13332         }
13333     }
13334 });
13335
13336 // private
13337 // hash to map config positions to true positions
13338 Roo.Resizable.positions = {
13339     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13340     hd: "hdrag"
13341 };
13342
13343 // private
13344 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13345     if(!this.tpl){
13346         // only initialize the template if resizable is used
13347         var tpl = Roo.DomHelper.createTemplate(
13348             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13349         );
13350         tpl.compile();
13351         Roo.Resizable.Handle.prototype.tpl = tpl;
13352     }
13353     this.position = pos;
13354     this.rz = rz;
13355     // show north drag fro topdra
13356     var handlepos = pos == 'hdrag' ? 'north' : pos;
13357     
13358     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13359     if (pos == 'hdrag') {
13360         this.el.setStyle('cursor', 'pointer');
13361     }
13362     this.el.unselectable();
13363     if(transparent){
13364         this.el.setOpacity(0);
13365     }
13366     this.el.on("mousedown", this.onMouseDown, this);
13367     if(!disableTrackOver){
13368         this.el.on("mouseover", this.onMouseOver, this);
13369         this.el.on("mouseout", this.onMouseOut, this);
13370     }
13371 };
13372
13373 // private
13374 Roo.Resizable.Handle.prototype = {
13375     afterResize : function(rz){
13376         Roo.log('after?');
13377         // do nothing
13378     },
13379     // private
13380     onMouseDown : function(e){
13381         this.rz.onMouseDown(this, e);
13382     },
13383     // private
13384     onMouseOver : function(e){
13385         this.rz.handleOver(this, e);
13386     },
13387     // private
13388     onMouseOut : function(e){
13389         this.rz.handleOut(this, e);
13390     }
13391 };/*
13392  * Based on:
13393  * Ext JS Library 1.1.1
13394  * Copyright(c) 2006-2007, Ext JS, LLC.
13395  *
13396  * Originally Released Under LGPL - original licence link has changed is not relivant.
13397  *
13398  * Fork - LGPL
13399  * <script type="text/javascript">
13400  */
13401
13402 /**
13403  * @class Roo.Editor
13404  * @extends Roo.Component
13405  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13406  * @constructor
13407  * Create a new Editor
13408  * @param {Roo.form.Field} field The Field object (or descendant)
13409  * @param {Object} config The config object
13410  */
13411 Roo.Editor = function(field, config){
13412     Roo.Editor.superclass.constructor.call(this, config);
13413     this.field = field;
13414     this.addEvents({
13415         /**
13416              * @event beforestartedit
13417              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13418              * false from the handler of this event.
13419              * @param {Editor} this
13420              * @param {Roo.Element} boundEl The underlying element bound to this editor
13421              * @param {Mixed} value The field value being set
13422              */
13423         "beforestartedit" : true,
13424         /**
13425              * @event startedit
13426              * Fires when this editor is displayed
13427              * @param {Roo.Element} boundEl The underlying element bound to this editor
13428              * @param {Mixed} value The starting field value
13429              */
13430         "startedit" : true,
13431         /**
13432              * @event beforecomplete
13433              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13434              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13435              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13436              * event will not fire since no edit actually occurred.
13437              * @param {Editor} this
13438              * @param {Mixed} value The current field value
13439              * @param {Mixed} startValue The original field value
13440              */
13441         "beforecomplete" : true,
13442         /**
13443              * @event complete
13444              * Fires after editing is complete and any changed value has been written to the underlying field.
13445              * @param {Editor} this
13446              * @param {Mixed} value The current field value
13447              * @param {Mixed} startValue The original field value
13448              */
13449         "complete" : true,
13450         /**
13451          * @event specialkey
13452          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13453          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13454          * @param {Roo.form.Field} this
13455          * @param {Roo.EventObject} e The event object
13456          */
13457         "specialkey" : true
13458     });
13459 };
13460
13461 Roo.extend(Roo.Editor, Roo.Component, {
13462     /**
13463      * @cfg {Boolean/String} autosize
13464      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13465      * or "height" to adopt the height only (defaults to false)
13466      */
13467     /**
13468      * @cfg {Boolean} revertInvalid
13469      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13470      * validation fails (defaults to true)
13471      */
13472     /**
13473      * @cfg {Boolean} ignoreNoChange
13474      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13475      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13476      * will never be ignored.
13477      */
13478     /**
13479      * @cfg {Boolean} hideEl
13480      * False to keep the bound element visible while the editor is displayed (defaults to true)
13481      */
13482     /**
13483      * @cfg {Mixed} value
13484      * The data value of the underlying field (defaults to "")
13485      */
13486     value : "",
13487     /**
13488      * @cfg {String} alignment
13489      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13490      */
13491     alignment: "c-c?",
13492     /**
13493      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13494      * for bottom-right shadow (defaults to "frame")
13495      */
13496     shadow : "frame",
13497     /**
13498      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13499      */
13500     constrain : false,
13501     /**
13502      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13503      */
13504     completeOnEnter : false,
13505     /**
13506      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13507      */
13508     cancelOnEsc : false,
13509     /**
13510      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13511      */
13512     updateEl : false,
13513
13514     // private
13515     onRender : function(ct, position){
13516         this.el = new Roo.Layer({
13517             shadow: this.shadow,
13518             cls: "x-editor",
13519             parentEl : ct,
13520             shim : this.shim,
13521             shadowOffset:4,
13522             id: this.id,
13523             constrain: this.constrain
13524         });
13525         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13526         if(this.field.msgTarget != 'title'){
13527             this.field.msgTarget = 'qtip';
13528         }
13529         this.field.render(this.el);
13530         if(Roo.isGecko){
13531             this.field.el.dom.setAttribute('autocomplete', 'off');
13532         }
13533         this.field.on("specialkey", this.onSpecialKey, this);
13534         if(this.swallowKeys){
13535             this.field.el.swallowEvent(['keydown','keypress']);
13536         }
13537         this.field.show();
13538         this.field.on("blur", this.onBlur, this);
13539         if(this.field.grow){
13540             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13541         }
13542     },
13543
13544     onSpecialKey : function(field, e)
13545     {
13546         //Roo.log('editor onSpecialKey');
13547         if(this.completeOnEnter && e.getKey() == e.ENTER){
13548             e.stopEvent();
13549             this.completeEdit();
13550             return;
13551         }
13552         // do not fire special key otherwise it might hide close the editor...
13553         if(e.getKey() == e.ENTER){    
13554             return;
13555         }
13556         if(this.cancelOnEsc && e.getKey() == e.ESC){
13557             this.cancelEdit();
13558             return;
13559         } 
13560         this.fireEvent('specialkey', field, e);
13561     
13562     },
13563
13564     /**
13565      * Starts the editing process and shows the editor.
13566      * @param {String/HTMLElement/Element} el The element to edit
13567      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13568       * to the innerHTML of el.
13569      */
13570     startEdit : function(el, value){
13571         if(this.editing){
13572             this.completeEdit();
13573         }
13574         this.boundEl = Roo.get(el);
13575         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13576         if(!this.rendered){
13577             this.render(this.parentEl || document.body);
13578         }
13579         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13580             return;
13581         }
13582         this.startValue = v;
13583         this.field.setValue(v);
13584         if(this.autoSize){
13585             var sz = this.boundEl.getSize();
13586             switch(this.autoSize){
13587                 case "width":
13588                 this.setSize(sz.width,  "");
13589                 break;
13590                 case "height":
13591                 this.setSize("",  sz.height);
13592                 break;
13593                 default:
13594                 this.setSize(sz.width,  sz.height);
13595             }
13596         }
13597         this.el.alignTo(this.boundEl, this.alignment);
13598         this.editing = true;
13599         if(Roo.QuickTips){
13600             Roo.QuickTips.disable();
13601         }
13602         this.show();
13603     },
13604
13605     /**
13606      * Sets the height and width of this editor.
13607      * @param {Number} width The new width
13608      * @param {Number} height The new height
13609      */
13610     setSize : function(w, h){
13611         this.field.setSize(w, h);
13612         if(this.el){
13613             this.el.sync();
13614         }
13615     },
13616
13617     /**
13618      * Realigns the editor to the bound field based on the current alignment config value.
13619      */
13620     realign : function(){
13621         this.el.alignTo(this.boundEl, this.alignment);
13622     },
13623
13624     /**
13625      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13626      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13627      */
13628     completeEdit : function(remainVisible){
13629         if(!this.editing){
13630             return;
13631         }
13632         var v = this.getValue();
13633         if(this.revertInvalid !== false && !this.field.isValid()){
13634             v = this.startValue;
13635             this.cancelEdit(true);
13636         }
13637         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13638             this.editing = false;
13639             this.hide();
13640             return;
13641         }
13642         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13643             this.editing = false;
13644             if(this.updateEl && this.boundEl){
13645                 this.boundEl.update(v);
13646             }
13647             if(remainVisible !== true){
13648                 this.hide();
13649             }
13650             this.fireEvent("complete", this, v, this.startValue);
13651         }
13652     },
13653
13654     // private
13655     onShow : function(){
13656         this.el.show();
13657         if(this.hideEl !== false){
13658             this.boundEl.hide();
13659         }
13660         this.field.show();
13661         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13662             this.fixIEFocus = true;
13663             this.deferredFocus.defer(50, this);
13664         }else{
13665             this.field.focus();
13666         }
13667         this.fireEvent("startedit", this.boundEl, this.startValue);
13668     },
13669
13670     deferredFocus : function(){
13671         if(this.editing){
13672             this.field.focus();
13673         }
13674     },
13675
13676     /**
13677      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13678      * reverted to the original starting value.
13679      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13680      * cancel (defaults to false)
13681      */
13682     cancelEdit : function(remainVisible){
13683         if(this.editing){
13684             this.setValue(this.startValue);
13685             if(remainVisible !== true){
13686                 this.hide();
13687             }
13688         }
13689     },
13690
13691     // private
13692     onBlur : function(){
13693         if(this.allowBlur !== true && this.editing){
13694             this.completeEdit();
13695         }
13696     },
13697
13698     // private
13699     onHide : function(){
13700         if(this.editing){
13701             this.completeEdit();
13702             return;
13703         }
13704         this.field.blur();
13705         if(this.field.collapse){
13706             this.field.collapse();
13707         }
13708         this.el.hide();
13709         if(this.hideEl !== false){
13710             this.boundEl.show();
13711         }
13712         if(Roo.QuickTips){
13713             Roo.QuickTips.enable();
13714         }
13715     },
13716
13717     /**
13718      * Sets the data value of the editor
13719      * @param {Mixed} value Any valid value supported by the underlying field
13720      */
13721     setValue : function(v){
13722         this.field.setValue(v);
13723     },
13724
13725     /**
13726      * Gets the data value of the editor
13727      * @return {Mixed} The data value
13728      */
13729     getValue : function(){
13730         return this.field.getValue();
13731     }
13732 });/*
13733  * Based on:
13734  * Ext JS Library 1.1.1
13735  * Copyright(c) 2006-2007, Ext JS, LLC.
13736  *
13737  * Originally Released Under LGPL - original licence link has changed is not relivant.
13738  *
13739  * Fork - LGPL
13740  * <script type="text/javascript">
13741  */
13742  
13743 /**
13744  * @class Roo.BasicDialog
13745  * @extends Roo.util.Observable
13746  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13747  * <pre><code>
13748 var dlg = new Roo.BasicDialog("my-dlg", {
13749     height: 200,
13750     width: 300,
13751     minHeight: 100,
13752     minWidth: 150,
13753     modal: true,
13754     proxyDrag: true,
13755     shadow: true
13756 });
13757 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13758 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13759 dlg.addButton('Cancel', dlg.hide, dlg);
13760 dlg.show();
13761 </code></pre>
13762   <b>A Dialog should always be a direct child of the body element.</b>
13763  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13764  * @cfg {String} title Default text to display in the title bar (defaults to null)
13765  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13766  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13767  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13768  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13769  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13770  * (defaults to null with no animation)
13771  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13772  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13773  * property for valid values (defaults to 'all')
13774  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13775  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13776  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13777  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13778  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13779  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13780  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13781  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13782  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13783  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13784  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13785  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13786  * draggable = true (defaults to false)
13787  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13788  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13789  * shadow (defaults to false)
13790  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13791  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13792  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13793  * @cfg {Array} buttons Array of buttons
13794  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13795  * @constructor
13796  * Create a new BasicDialog.
13797  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13798  * @param {Object} config Configuration options
13799  */
13800 Roo.BasicDialog = function(el, config){
13801     this.el = Roo.get(el);
13802     var dh = Roo.DomHelper;
13803     if(!this.el && config && config.autoCreate){
13804         if(typeof config.autoCreate == "object"){
13805             if(!config.autoCreate.id){
13806                 config.autoCreate.id = el;
13807             }
13808             this.el = dh.append(document.body,
13809                         config.autoCreate, true);
13810         }else{
13811             this.el = dh.append(document.body,
13812                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13813         }
13814     }
13815     el = this.el;
13816     el.setDisplayed(true);
13817     el.hide = this.hideAction;
13818     this.id = el.id;
13819     el.addClass("x-dlg");
13820
13821     Roo.apply(this, config);
13822
13823     this.proxy = el.createProxy("x-dlg-proxy");
13824     this.proxy.hide = this.hideAction;
13825     this.proxy.setOpacity(.5);
13826     this.proxy.hide();
13827
13828     if(config.width){
13829         el.setWidth(config.width);
13830     }
13831     if(config.height){
13832         el.setHeight(config.height);
13833     }
13834     this.size = el.getSize();
13835     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13836         this.xy = [config.x,config.y];
13837     }else{
13838         this.xy = el.getCenterXY(true);
13839     }
13840     /** The header element @type Roo.Element */
13841     this.header = el.child("> .x-dlg-hd");
13842     /** The body element @type Roo.Element */
13843     this.body = el.child("> .x-dlg-bd");
13844     /** The footer element @type Roo.Element */
13845     this.footer = el.child("> .x-dlg-ft");
13846
13847     if(!this.header){
13848         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13849     }
13850     if(!this.body){
13851         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13852     }
13853
13854     this.header.unselectable();
13855     if(this.title){
13856         this.header.update(this.title);
13857     }
13858     // this element allows the dialog to be focused for keyboard event
13859     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13860     this.focusEl.swallowEvent("click", true);
13861
13862     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13863
13864     // wrap the body and footer for special rendering
13865     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13866     if(this.footer){
13867         this.bwrap.dom.appendChild(this.footer.dom);
13868     }
13869
13870     this.bg = this.el.createChild({
13871         tag: "div", cls:"x-dlg-bg",
13872         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13873     });
13874     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13875
13876
13877     if(this.autoScroll !== false && !this.autoTabs){
13878         this.body.setStyle("overflow", "auto");
13879     }
13880
13881     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13882
13883     if(this.closable !== false){
13884         this.el.addClass("x-dlg-closable");
13885         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13886         this.close.on("click", this.closeClick, this);
13887         this.close.addClassOnOver("x-dlg-close-over");
13888     }
13889     if(this.collapsible !== false){
13890         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13891         this.collapseBtn.on("click", this.collapseClick, this);
13892         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13893         this.header.on("dblclick", this.collapseClick, this);
13894     }
13895     if(this.resizable !== false){
13896         this.el.addClass("x-dlg-resizable");
13897         this.resizer = new Roo.Resizable(el, {
13898             minWidth: this.minWidth || 80,
13899             minHeight:this.minHeight || 80,
13900             handles: this.resizeHandles || "all",
13901             pinned: true
13902         });
13903         this.resizer.on("beforeresize", this.beforeResize, this);
13904         this.resizer.on("resize", this.onResize, this);
13905     }
13906     if(this.draggable !== false){
13907         el.addClass("x-dlg-draggable");
13908         if (!this.proxyDrag) {
13909             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13910         }
13911         else {
13912             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13913         }
13914         dd.setHandleElId(this.header.id);
13915         dd.endDrag = this.endMove.createDelegate(this);
13916         dd.startDrag = this.startMove.createDelegate(this);
13917         dd.onDrag = this.onDrag.createDelegate(this);
13918         dd.scroll = false;
13919         this.dd = dd;
13920     }
13921     if(this.modal){
13922         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13923         this.mask.enableDisplayMode("block");
13924         this.mask.hide();
13925         this.el.addClass("x-dlg-modal");
13926     }
13927     if(this.shadow){
13928         this.shadow = new Roo.Shadow({
13929             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13930             offset : this.shadowOffset
13931         });
13932     }else{
13933         this.shadowOffset = 0;
13934     }
13935     if(Roo.useShims && this.shim !== false){
13936         this.shim = this.el.createShim();
13937         this.shim.hide = this.hideAction;
13938         this.shim.hide();
13939     }else{
13940         this.shim = false;
13941     }
13942     if(this.autoTabs){
13943         this.initTabs();
13944     }
13945     if (this.buttons) { 
13946         var bts= this.buttons;
13947         this.buttons = [];
13948         Roo.each(bts, function(b) {
13949             this.addButton(b);
13950         }, this);
13951     }
13952     
13953     
13954     this.addEvents({
13955         /**
13956          * @event keydown
13957          * Fires when a key is pressed
13958          * @param {Roo.BasicDialog} this
13959          * @param {Roo.EventObject} e
13960          */
13961         "keydown" : true,
13962         /**
13963          * @event move
13964          * Fires when this dialog is moved by the user.
13965          * @param {Roo.BasicDialog} this
13966          * @param {Number} x The new page X
13967          * @param {Number} y The new page Y
13968          */
13969         "move" : true,
13970         /**
13971          * @event resize
13972          * Fires when this dialog is resized by the user.
13973          * @param {Roo.BasicDialog} this
13974          * @param {Number} width The new width
13975          * @param {Number} height The new height
13976          */
13977         "resize" : true,
13978         /**
13979          * @event beforehide
13980          * Fires before this dialog is hidden.
13981          * @param {Roo.BasicDialog} this
13982          */
13983         "beforehide" : true,
13984         /**
13985          * @event hide
13986          * Fires when this dialog is hidden.
13987          * @param {Roo.BasicDialog} this
13988          */
13989         "hide" : true,
13990         /**
13991          * @event beforeshow
13992          * Fires before this dialog is shown.
13993          * @param {Roo.BasicDialog} this
13994          */
13995         "beforeshow" : true,
13996         /**
13997          * @event show
13998          * Fires when this dialog is shown.
13999          * @param {Roo.BasicDialog} this
14000          */
14001         "show" : true
14002     });
14003     el.on("keydown", this.onKeyDown, this);
14004     el.on("mousedown", this.toFront, this);
14005     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14006     this.el.hide();
14007     Roo.DialogManager.register(this);
14008     Roo.BasicDialog.superclass.constructor.call(this);
14009 };
14010
14011 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14012     shadowOffset: Roo.isIE ? 6 : 5,
14013     minHeight: 80,
14014     minWidth: 200,
14015     minButtonWidth: 75,
14016     defaultButton: null,
14017     buttonAlign: "right",
14018     tabTag: 'div',
14019     firstShow: true,
14020
14021     /**
14022      * Sets the dialog title text
14023      * @param {String} text The title text to display
14024      * @return {Roo.BasicDialog} this
14025      */
14026     setTitle : function(text){
14027         this.header.update(text);
14028         return this;
14029     },
14030
14031     // private
14032     closeClick : function(){
14033         this.hide();
14034     },
14035
14036     // private
14037     collapseClick : function(){
14038         this[this.collapsed ? "expand" : "collapse"]();
14039     },
14040
14041     /**
14042      * Collapses the dialog to its minimized state (only the title bar is visible).
14043      * Equivalent to the user clicking the collapse dialog button.
14044      */
14045     collapse : function(){
14046         if(!this.collapsed){
14047             this.collapsed = true;
14048             this.el.addClass("x-dlg-collapsed");
14049             this.restoreHeight = this.el.getHeight();
14050             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14051         }
14052     },
14053
14054     /**
14055      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14056      * clicking the expand dialog button.
14057      */
14058     expand : function(){
14059         if(this.collapsed){
14060             this.collapsed = false;
14061             this.el.removeClass("x-dlg-collapsed");
14062             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14063         }
14064     },
14065
14066     /**
14067      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14068      * @return {Roo.TabPanel} The tabs component
14069      */
14070     initTabs : function(){
14071         var tabs = this.getTabs();
14072         while(tabs.getTab(0)){
14073             tabs.removeTab(0);
14074         }
14075         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14076             var dom = el.dom;
14077             tabs.addTab(Roo.id(dom), dom.title);
14078             dom.title = "";
14079         });
14080         tabs.activate(0);
14081         return tabs;
14082     },
14083
14084     // private
14085     beforeResize : function(){
14086         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14087     },
14088
14089     // private
14090     onResize : function(){
14091         this.refreshSize();
14092         this.syncBodyHeight();
14093         this.adjustAssets();
14094         this.focus();
14095         this.fireEvent("resize", this, this.size.width, this.size.height);
14096     },
14097
14098     // private
14099     onKeyDown : function(e){
14100         if(this.isVisible()){
14101             this.fireEvent("keydown", this, e);
14102         }
14103     },
14104
14105     /**
14106      * Resizes the dialog.
14107      * @param {Number} width
14108      * @param {Number} height
14109      * @return {Roo.BasicDialog} this
14110      */
14111     resizeTo : function(width, height){
14112         this.el.setSize(width, height);
14113         this.size = {width: width, height: height};
14114         this.syncBodyHeight();
14115         if(this.fixedcenter){
14116             this.center();
14117         }
14118         if(this.isVisible()){
14119             this.constrainXY();
14120             this.adjustAssets();
14121         }
14122         this.fireEvent("resize", this, width, height);
14123         return this;
14124     },
14125
14126
14127     /**
14128      * Resizes the dialog to fit the specified content size.
14129      * @param {Number} width
14130      * @param {Number} height
14131      * @return {Roo.BasicDialog} this
14132      */
14133     setContentSize : function(w, h){
14134         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14135         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14136         //if(!this.el.isBorderBox()){
14137             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14138             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14139         //}
14140         if(this.tabs){
14141             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14142             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14143         }
14144         this.resizeTo(w, h);
14145         return this;
14146     },
14147
14148     /**
14149      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14150      * executed in response to a particular key being pressed while the dialog is active.
14151      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14152      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14153      * @param {Function} fn The function to call
14154      * @param {Object} scope (optional) The scope of the function
14155      * @return {Roo.BasicDialog} this
14156      */
14157     addKeyListener : function(key, fn, scope){
14158         var keyCode, shift, ctrl, alt;
14159         if(typeof key == "object" && !(key instanceof Array)){
14160             keyCode = key["key"];
14161             shift = key["shift"];
14162             ctrl = key["ctrl"];
14163             alt = key["alt"];
14164         }else{
14165             keyCode = key;
14166         }
14167         var handler = function(dlg, e){
14168             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14169                 var k = e.getKey();
14170                 if(keyCode instanceof Array){
14171                     for(var i = 0, len = keyCode.length; i < len; i++){
14172                         if(keyCode[i] == k){
14173                           fn.call(scope || window, dlg, k, e);
14174                           return;
14175                         }
14176                     }
14177                 }else{
14178                     if(k == keyCode){
14179                         fn.call(scope || window, dlg, k, e);
14180                     }
14181                 }
14182             }
14183         };
14184         this.on("keydown", handler);
14185         return this;
14186     },
14187
14188     /**
14189      * Returns the TabPanel component (creates it if it doesn't exist).
14190      * Note: If you wish to simply check for the existence of tabs without creating them,
14191      * check for a null 'tabs' property.
14192      * @return {Roo.TabPanel} The tabs component
14193      */
14194     getTabs : function(){
14195         if(!this.tabs){
14196             this.el.addClass("x-dlg-auto-tabs");
14197             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14198             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14199         }
14200         return this.tabs;
14201     },
14202
14203     /**
14204      * Adds a button to the footer section of the dialog.
14205      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14206      * object or a valid Roo.DomHelper element config
14207      * @param {Function} handler The function called when the button is clicked
14208      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14209      * @return {Roo.Button} The new button
14210      */
14211     addButton : function(config, handler, scope){
14212         var dh = Roo.DomHelper;
14213         if(!this.footer){
14214             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14215         }
14216         if(!this.btnContainer){
14217             var tb = this.footer.createChild({
14218
14219                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14220                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14221             }, null, true);
14222             this.btnContainer = tb.firstChild.firstChild.firstChild;
14223         }
14224         var bconfig = {
14225             handler: handler,
14226             scope: scope,
14227             minWidth: this.minButtonWidth,
14228             hideParent:true
14229         };
14230         if(typeof config == "string"){
14231             bconfig.text = config;
14232         }else{
14233             if(config.tag){
14234                 bconfig.dhconfig = config;
14235             }else{
14236                 Roo.apply(bconfig, config);
14237             }
14238         }
14239         var fc = false;
14240         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14241             bconfig.position = Math.max(0, bconfig.position);
14242             fc = this.btnContainer.childNodes[bconfig.position];
14243         }
14244          
14245         var btn = new Roo.Button(
14246             fc ? 
14247                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14248                 : this.btnContainer.appendChild(document.createElement("td")),
14249             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14250             bconfig
14251         );
14252         this.syncBodyHeight();
14253         if(!this.buttons){
14254             /**
14255              * Array of all the buttons that have been added to this dialog via addButton
14256              * @type Array
14257              */
14258             this.buttons = [];
14259         }
14260         this.buttons.push(btn);
14261         return btn;
14262     },
14263
14264     /**
14265      * Sets the default button to be focused when the dialog is displayed.
14266      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14267      * @return {Roo.BasicDialog} this
14268      */
14269     setDefaultButton : function(btn){
14270         this.defaultButton = btn;
14271         return this;
14272     },
14273
14274     // private
14275     getHeaderFooterHeight : function(safe){
14276         var height = 0;
14277         if(this.header){
14278            height += this.header.getHeight();
14279         }
14280         if(this.footer){
14281            var fm = this.footer.getMargins();
14282             height += (this.footer.getHeight()+fm.top+fm.bottom);
14283         }
14284         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14285         height += this.centerBg.getPadding("tb");
14286         return height;
14287     },
14288
14289     // private
14290     syncBodyHeight : function()
14291     {
14292         var bd = this.body, // the text
14293             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14294             bw = this.bwrap;
14295         var height = this.size.height - this.getHeaderFooterHeight(false);
14296         bd.setHeight(height-bd.getMargins("tb"));
14297         var hh = this.header.getHeight();
14298         var h = this.size.height-hh;
14299         cb.setHeight(h);
14300         
14301         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14302         bw.setHeight(h-cb.getPadding("tb"));
14303         
14304         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14305         bd.setWidth(bw.getWidth(true));
14306         if(this.tabs){
14307             this.tabs.syncHeight();
14308             if(Roo.isIE){
14309                 this.tabs.el.repaint();
14310             }
14311         }
14312     },
14313
14314     /**
14315      * Restores the previous state of the dialog if Roo.state is configured.
14316      * @return {Roo.BasicDialog} this
14317      */
14318     restoreState : function(){
14319         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14320         if(box && box.width){
14321             this.xy = [box.x, box.y];
14322             this.resizeTo(box.width, box.height);
14323         }
14324         return this;
14325     },
14326
14327     // private
14328     beforeShow : function(){
14329         this.expand();
14330         if(this.fixedcenter){
14331             this.xy = this.el.getCenterXY(true);
14332         }
14333         if(this.modal){
14334             Roo.get(document.body).addClass("x-body-masked");
14335             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14336             this.mask.show();
14337         }
14338         this.constrainXY();
14339     },
14340
14341     // private
14342     animShow : function(){
14343         var b = Roo.get(this.animateTarget).getBox();
14344         this.proxy.setSize(b.width, b.height);
14345         this.proxy.setLocation(b.x, b.y);
14346         this.proxy.show();
14347         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14348                     true, .35, this.showEl.createDelegate(this));
14349     },
14350
14351     /**
14352      * Shows the dialog.
14353      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14354      * @return {Roo.BasicDialog} this
14355      */
14356     show : function(animateTarget){
14357         if (this.fireEvent("beforeshow", this) === false){
14358             return;
14359         }
14360         if(this.syncHeightBeforeShow){
14361             this.syncBodyHeight();
14362         }else if(this.firstShow){
14363             this.firstShow = false;
14364             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14365         }
14366         this.animateTarget = animateTarget || this.animateTarget;
14367         if(!this.el.isVisible()){
14368             this.beforeShow();
14369             if(this.animateTarget && Roo.get(this.animateTarget)){
14370                 this.animShow();
14371             }else{
14372                 this.showEl();
14373             }
14374         }
14375         return this;
14376     },
14377
14378     // private
14379     showEl : function(){
14380         this.proxy.hide();
14381         this.el.setXY(this.xy);
14382         this.el.show();
14383         this.adjustAssets(true);
14384         this.toFront();
14385         this.focus();
14386         // IE peekaboo bug - fix found by Dave Fenwick
14387         if(Roo.isIE){
14388             this.el.repaint();
14389         }
14390         this.fireEvent("show", this);
14391     },
14392
14393     /**
14394      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14395      * dialog itself will receive focus.
14396      */
14397     focus : function(){
14398         if(this.defaultButton){
14399             this.defaultButton.focus();
14400         }else{
14401             this.focusEl.focus();
14402         }
14403     },
14404
14405     // private
14406     constrainXY : function(){
14407         if(this.constraintoviewport !== false){
14408             if(!this.viewSize){
14409                 if(this.container){
14410                     var s = this.container.getSize();
14411                     this.viewSize = [s.width, s.height];
14412                 }else{
14413                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14414                 }
14415             }
14416             var s = Roo.get(this.container||document).getScroll();
14417
14418             var x = this.xy[0], y = this.xy[1];
14419             var w = this.size.width, h = this.size.height;
14420             var vw = this.viewSize[0], vh = this.viewSize[1];
14421             // only move it if it needs it
14422             var moved = false;
14423             // first validate right/bottom
14424             if(x + w > vw+s.left){
14425                 x = vw - w;
14426                 moved = true;
14427             }
14428             if(y + h > vh+s.top){
14429                 y = vh - h;
14430                 moved = true;
14431             }
14432             // then make sure top/left isn't negative
14433             if(x < s.left){
14434                 x = s.left;
14435                 moved = true;
14436             }
14437             if(y < s.top){
14438                 y = s.top;
14439                 moved = true;
14440             }
14441             if(moved){
14442                 // cache xy
14443                 this.xy = [x, y];
14444                 if(this.isVisible()){
14445                     this.el.setLocation(x, y);
14446                     this.adjustAssets();
14447                 }
14448             }
14449         }
14450     },
14451
14452     // private
14453     onDrag : function(){
14454         if(!this.proxyDrag){
14455             this.xy = this.el.getXY();
14456             this.adjustAssets();
14457         }
14458     },
14459
14460     // private
14461     adjustAssets : function(doShow){
14462         var x = this.xy[0], y = this.xy[1];
14463         var w = this.size.width, h = this.size.height;
14464         if(doShow === true){
14465             if(this.shadow){
14466                 this.shadow.show(this.el);
14467             }
14468             if(this.shim){
14469                 this.shim.show();
14470             }
14471         }
14472         if(this.shadow && this.shadow.isVisible()){
14473             this.shadow.show(this.el);
14474         }
14475         if(this.shim && this.shim.isVisible()){
14476             this.shim.setBounds(x, y, w, h);
14477         }
14478     },
14479
14480     // private
14481     adjustViewport : function(w, h){
14482         if(!w || !h){
14483             w = Roo.lib.Dom.getViewWidth();
14484             h = Roo.lib.Dom.getViewHeight();
14485         }
14486         // cache the size
14487         this.viewSize = [w, h];
14488         if(this.modal && this.mask.isVisible()){
14489             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14490             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14491         }
14492         if(this.isVisible()){
14493             this.constrainXY();
14494         }
14495     },
14496
14497     /**
14498      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14499      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14500      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14501      */
14502     destroy : function(removeEl){
14503         if(this.isVisible()){
14504             this.animateTarget = null;
14505             this.hide();
14506         }
14507         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14508         if(this.tabs){
14509             this.tabs.destroy(removeEl);
14510         }
14511         Roo.destroy(
14512              this.shim,
14513              this.proxy,
14514              this.resizer,
14515              this.close,
14516              this.mask
14517         );
14518         if(this.dd){
14519             this.dd.unreg();
14520         }
14521         if(this.buttons){
14522            for(var i = 0, len = this.buttons.length; i < len; i++){
14523                this.buttons[i].destroy();
14524            }
14525         }
14526         this.el.removeAllListeners();
14527         if(removeEl === true){
14528             this.el.update("");
14529             this.el.remove();
14530         }
14531         Roo.DialogManager.unregister(this);
14532     },
14533
14534     // private
14535     startMove : function(){
14536         if(this.proxyDrag){
14537             this.proxy.show();
14538         }
14539         if(this.constraintoviewport !== false){
14540             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14541         }
14542     },
14543
14544     // private
14545     endMove : function(){
14546         if(!this.proxyDrag){
14547             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14548         }else{
14549             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14550             this.proxy.hide();
14551         }
14552         this.refreshSize();
14553         this.adjustAssets();
14554         this.focus();
14555         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14556     },
14557
14558     /**
14559      * Brings this dialog to the front of any other visible dialogs
14560      * @return {Roo.BasicDialog} this
14561      */
14562     toFront : function(){
14563         Roo.DialogManager.bringToFront(this);
14564         return this;
14565     },
14566
14567     /**
14568      * Sends this dialog to the back (under) of any other visible dialogs
14569      * @return {Roo.BasicDialog} this
14570      */
14571     toBack : function(){
14572         Roo.DialogManager.sendToBack(this);
14573         return this;
14574     },
14575
14576     /**
14577      * Centers this dialog in the viewport
14578      * @return {Roo.BasicDialog} this
14579      */
14580     center : function(){
14581         var xy = this.el.getCenterXY(true);
14582         this.moveTo(xy[0], xy[1]);
14583         return this;
14584     },
14585
14586     /**
14587      * Moves the dialog's top-left corner to the specified point
14588      * @param {Number} x
14589      * @param {Number} y
14590      * @return {Roo.BasicDialog} this
14591      */
14592     moveTo : function(x, y){
14593         this.xy = [x,y];
14594         if(this.isVisible()){
14595             this.el.setXY(this.xy);
14596             this.adjustAssets();
14597         }
14598         return this;
14599     },
14600
14601     /**
14602      * Aligns the dialog to the specified element
14603      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14604      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14605      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14606      * @return {Roo.BasicDialog} this
14607      */
14608     alignTo : function(element, position, offsets){
14609         this.xy = this.el.getAlignToXY(element, position, offsets);
14610         if(this.isVisible()){
14611             this.el.setXY(this.xy);
14612             this.adjustAssets();
14613         }
14614         return this;
14615     },
14616
14617     /**
14618      * Anchors an element to another element and realigns it when the window is resized.
14619      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14620      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14621      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14622      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14623      * is a number, it is used as the buffer delay (defaults to 50ms).
14624      * @return {Roo.BasicDialog} this
14625      */
14626     anchorTo : function(el, alignment, offsets, monitorScroll){
14627         var action = function(){
14628             this.alignTo(el, alignment, offsets);
14629         };
14630         Roo.EventManager.onWindowResize(action, this);
14631         var tm = typeof monitorScroll;
14632         if(tm != 'undefined'){
14633             Roo.EventManager.on(window, 'scroll', action, this,
14634                 {buffer: tm == 'number' ? monitorScroll : 50});
14635         }
14636         action.call(this);
14637         return this;
14638     },
14639
14640     /**
14641      * Returns true if the dialog is visible
14642      * @return {Boolean}
14643      */
14644     isVisible : function(){
14645         return this.el.isVisible();
14646     },
14647
14648     // private
14649     animHide : function(callback){
14650         var b = Roo.get(this.animateTarget).getBox();
14651         this.proxy.show();
14652         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14653         this.el.hide();
14654         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14655                     this.hideEl.createDelegate(this, [callback]));
14656     },
14657
14658     /**
14659      * Hides the dialog.
14660      * @param {Function} callback (optional) Function to call when the dialog is hidden
14661      * @return {Roo.BasicDialog} this
14662      */
14663     hide : function(callback){
14664         if (this.fireEvent("beforehide", this) === false){
14665             return;
14666         }
14667         if(this.shadow){
14668             this.shadow.hide();
14669         }
14670         if(this.shim) {
14671           this.shim.hide();
14672         }
14673         // sometimes animateTarget seems to get set.. causing problems...
14674         // this just double checks..
14675         if(this.animateTarget && Roo.get(this.animateTarget)) {
14676            this.animHide(callback);
14677         }else{
14678             this.el.hide();
14679             this.hideEl(callback);
14680         }
14681         return this;
14682     },
14683
14684     // private
14685     hideEl : function(callback){
14686         this.proxy.hide();
14687         if(this.modal){
14688             this.mask.hide();
14689             Roo.get(document.body).removeClass("x-body-masked");
14690         }
14691         this.fireEvent("hide", this);
14692         if(typeof callback == "function"){
14693             callback();
14694         }
14695     },
14696
14697     // private
14698     hideAction : function(){
14699         this.setLeft("-10000px");
14700         this.setTop("-10000px");
14701         this.setStyle("visibility", "hidden");
14702     },
14703
14704     // private
14705     refreshSize : function(){
14706         this.size = this.el.getSize();
14707         this.xy = this.el.getXY();
14708         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14709     },
14710
14711     // private
14712     // z-index is managed by the DialogManager and may be overwritten at any time
14713     setZIndex : function(index){
14714         if(this.modal){
14715             this.mask.setStyle("z-index", index);
14716         }
14717         if(this.shim){
14718             this.shim.setStyle("z-index", ++index);
14719         }
14720         if(this.shadow){
14721             this.shadow.setZIndex(++index);
14722         }
14723         this.el.setStyle("z-index", ++index);
14724         if(this.proxy){
14725             this.proxy.setStyle("z-index", ++index);
14726         }
14727         if(this.resizer){
14728             this.resizer.proxy.setStyle("z-index", ++index);
14729         }
14730
14731         this.lastZIndex = index;
14732     },
14733
14734     /**
14735      * Returns the element for this dialog
14736      * @return {Roo.Element} The underlying dialog Element
14737      */
14738     getEl : function(){
14739         return this.el;
14740     }
14741 });
14742
14743 /**
14744  * @class Roo.DialogManager
14745  * Provides global access to BasicDialogs that have been created and
14746  * support for z-indexing (layering) multiple open dialogs.
14747  */
14748 Roo.DialogManager = function(){
14749     var list = {};
14750     var accessList = [];
14751     var front = null;
14752
14753     // private
14754     var sortDialogs = function(d1, d2){
14755         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14756     };
14757
14758     // private
14759     var orderDialogs = function(){
14760         accessList.sort(sortDialogs);
14761         var seed = Roo.DialogManager.zseed;
14762         for(var i = 0, len = accessList.length; i < len; i++){
14763             var dlg = accessList[i];
14764             if(dlg){
14765                 dlg.setZIndex(seed + (i*10));
14766             }
14767         }
14768     };
14769
14770     return {
14771         /**
14772          * The starting z-index for BasicDialogs (defaults to 9000)
14773          * @type Number The z-index value
14774          */
14775         zseed : 9000,
14776
14777         // private
14778         register : function(dlg){
14779             list[dlg.id] = dlg;
14780             accessList.push(dlg);
14781         },
14782
14783         // private
14784         unregister : function(dlg){
14785             delete list[dlg.id];
14786             var i=0;
14787             var len=0;
14788             if(!accessList.indexOf){
14789                 for(  i = 0, len = accessList.length; i < len; i++){
14790                     if(accessList[i] == dlg){
14791                         accessList.splice(i, 1);
14792                         return;
14793                     }
14794                 }
14795             }else{
14796                  i = accessList.indexOf(dlg);
14797                 if(i != -1){
14798                     accessList.splice(i, 1);
14799                 }
14800             }
14801         },
14802
14803         /**
14804          * Gets a registered dialog by id
14805          * @param {String/Object} id The id of the dialog or a dialog
14806          * @return {Roo.BasicDialog} this
14807          */
14808         get : function(id){
14809             return typeof id == "object" ? id : list[id];
14810         },
14811
14812         /**
14813          * Brings the specified dialog to the front
14814          * @param {String/Object} dlg The id of the dialog or a dialog
14815          * @return {Roo.BasicDialog} this
14816          */
14817         bringToFront : function(dlg){
14818             dlg = this.get(dlg);
14819             if(dlg != front){
14820                 front = dlg;
14821                 dlg._lastAccess = new Date().getTime();
14822                 orderDialogs();
14823             }
14824             return dlg;
14825         },
14826
14827         /**
14828          * Sends the specified dialog to the back
14829          * @param {String/Object} dlg The id of the dialog or a dialog
14830          * @return {Roo.BasicDialog} this
14831          */
14832         sendToBack : function(dlg){
14833             dlg = this.get(dlg);
14834             dlg._lastAccess = -(new Date().getTime());
14835             orderDialogs();
14836             return dlg;
14837         },
14838
14839         /**
14840          * Hides all dialogs
14841          */
14842         hideAll : function(){
14843             for(var id in list){
14844                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14845                     list[id].hide();
14846                 }
14847             }
14848         }
14849     };
14850 }();
14851
14852 /**
14853  * @class Roo.LayoutDialog
14854  * @extends Roo.BasicDialog
14855  * Dialog which provides adjustments for working with a layout in a Dialog.
14856  * Add your necessary layout config options to the dialog's config.<br>
14857  * Example usage (including a nested layout):
14858  * <pre><code>
14859 if(!dialog){
14860     dialog = new Roo.LayoutDialog("download-dlg", {
14861         modal: true,
14862         width:600,
14863         height:450,
14864         shadow:true,
14865         minWidth:500,
14866         minHeight:350,
14867         autoTabs:true,
14868         proxyDrag:true,
14869         // layout config merges with the dialog config
14870         center:{
14871             tabPosition: "top",
14872             alwaysShowTabs: true
14873         }
14874     });
14875     dialog.addKeyListener(27, dialog.hide, dialog);
14876     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14877     dialog.addButton("Build It!", this.getDownload, this);
14878
14879     // we can even add nested layouts
14880     var innerLayout = new Roo.BorderLayout("dl-inner", {
14881         east: {
14882             initialSize: 200,
14883             autoScroll:true,
14884             split:true
14885         },
14886         center: {
14887             autoScroll:true
14888         }
14889     });
14890     innerLayout.beginUpdate();
14891     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14892     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14893     innerLayout.endUpdate(true);
14894
14895     var layout = dialog.getLayout();
14896     layout.beginUpdate();
14897     layout.add("center", new Roo.ContentPanel("standard-panel",
14898                         {title: "Download the Source", fitToFrame:true}));
14899     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14900                {title: "Build your own roo.js"}));
14901     layout.getRegion("center").showPanel(sp);
14902     layout.endUpdate();
14903 }
14904 </code></pre>
14905     * @constructor
14906     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14907     * @param {Object} config configuration options
14908   */
14909 Roo.LayoutDialog = function(el, cfg){
14910     
14911     var config=  cfg;
14912     if (typeof(cfg) == 'undefined') {
14913         config = Roo.apply({}, el);
14914         // not sure why we use documentElement here.. - it should always be body.
14915         // IE7 borks horribly if we use documentElement.
14916         // webkit also does not like documentElement - it creates a body element...
14917         el = Roo.get( document.body || document.documentElement ).createChild();
14918         //config.autoCreate = true;
14919     }
14920     
14921     
14922     config.autoTabs = false;
14923     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14924     this.body.setStyle({overflow:"hidden", position:"relative"});
14925     this.layout = new Roo.BorderLayout(this.body.dom, config);
14926     this.layout.monitorWindowResize = false;
14927     this.el.addClass("x-dlg-auto-layout");
14928     // fix case when center region overwrites center function
14929     this.center = Roo.BasicDialog.prototype.center;
14930     this.on("show", this.layout.layout, this.layout, true);
14931     if (config.items) {
14932         var xitems = config.items;
14933         delete config.items;
14934         Roo.each(xitems, this.addxtype, this);
14935     }
14936     
14937     
14938 };
14939 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14940     /**
14941      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14942      * @deprecated
14943      */
14944     endUpdate : function(){
14945         this.layout.endUpdate();
14946     },
14947
14948     /**
14949      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14950      *  @deprecated
14951      */
14952     beginUpdate : function(){
14953         this.layout.beginUpdate();
14954     },
14955
14956     /**
14957      * Get the BorderLayout for this dialog
14958      * @return {Roo.BorderLayout}
14959      */
14960     getLayout : function(){
14961         return this.layout;
14962     },
14963
14964     showEl : function(){
14965         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14966         if(Roo.isIE7){
14967             this.layout.layout();
14968         }
14969     },
14970
14971     // private
14972     // Use the syncHeightBeforeShow config option to control this automatically
14973     syncBodyHeight : function(){
14974         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14975         if(this.layout){this.layout.layout();}
14976     },
14977     
14978       /**
14979      * Add an xtype element (actually adds to the layout.)
14980      * @return {Object} xdata xtype object data.
14981      */
14982     
14983     addxtype : function(c) {
14984         return this.layout.addxtype(c);
14985     }
14986 });/*
14987  * Based on:
14988  * Ext JS Library 1.1.1
14989  * Copyright(c) 2006-2007, Ext JS, LLC.
14990  *
14991  * Originally Released Under LGPL - original licence link has changed is not relivant.
14992  *
14993  * Fork - LGPL
14994  * <script type="text/javascript">
14995  */
14996  
14997 /**
14998  * @class Roo.MessageBox
14999  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15000  * Example usage:
15001  *<pre><code>
15002 // Basic alert:
15003 Roo.Msg.alert('Status', 'Changes saved successfully.');
15004
15005 // Prompt for user data:
15006 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15007     if (btn == 'ok'){
15008         // process text value...
15009     }
15010 });
15011
15012 // Show a dialog using config options:
15013 Roo.Msg.show({
15014    title:'Save Changes?',
15015    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15016    buttons: Roo.Msg.YESNOCANCEL,
15017    fn: processResult,
15018    animEl: 'elId'
15019 });
15020 </code></pre>
15021  * @singleton
15022  */
15023 Roo.MessageBox = function(){
15024     var dlg, opt, mask, waitTimer;
15025     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15026     var buttons, activeTextEl, bwidth;
15027
15028     // private
15029     var handleButton = function(button){
15030         dlg.hide();
15031         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15032     };
15033
15034     // private
15035     var handleHide = function(){
15036         if(opt && opt.cls){
15037             dlg.el.removeClass(opt.cls);
15038         }
15039         if(waitTimer){
15040             Roo.TaskMgr.stop(waitTimer);
15041             waitTimer = null;
15042         }
15043     };
15044
15045     // private
15046     var updateButtons = function(b){
15047         var width = 0;
15048         if(!b){
15049             buttons["ok"].hide();
15050             buttons["cancel"].hide();
15051             buttons["yes"].hide();
15052             buttons["no"].hide();
15053             dlg.footer.dom.style.display = 'none';
15054             return width;
15055         }
15056         dlg.footer.dom.style.display = '';
15057         for(var k in buttons){
15058             if(typeof buttons[k] != "function"){
15059                 if(b[k]){
15060                     buttons[k].show();
15061                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15062                     width += buttons[k].el.getWidth()+15;
15063                 }else{
15064                     buttons[k].hide();
15065                 }
15066             }
15067         }
15068         return width;
15069     };
15070
15071     // private
15072     var handleEsc = function(d, k, e){
15073         if(opt && opt.closable !== false){
15074             dlg.hide();
15075         }
15076         if(e){
15077             e.stopEvent();
15078         }
15079     };
15080
15081     return {
15082         /**
15083          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15084          * @return {Roo.BasicDialog} The BasicDialog element
15085          */
15086         getDialog : function(){
15087            if(!dlg){
15088                 dlg = new Roo.BasicDialog("x-msg-box", {
15089                     autoCreate : true,
15090                     shadow: true,
15091                     draggable: true,
15092                     resizable:false,
15093                     constraintoviewport:false,
15094                     fixedcenter:true,
15095                     collapsible : false,
15096                     shim:true,
15097                     modal: true,
15098                     width:400, height:100,
15099                     buttonAlign:"center",
15100                     closeClick : function(){
15101                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15102                             handleButton("no");
15103                         }else{
15104                             handleButton("cancel");
15105                         }
15106                     }
15107                 });
15108                 dlg.on("hide", handleHide);
15109                 mask = dlg.mask;
15110                 dlg.addKeyListener(27, handleEsc);
15111                 buttons = {};
15112                 var bt = this.buttonText;
15113                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15114                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15115                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15116                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15117                 bodyEl = dlg.body.createChild({
15118
15119                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15120                 });
15121                 msgEl = bodyEl.dom.firstChild;
15122                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15123                 textboxEl.enableDisplayMode();
15124                 textboxEl.addKeyListener([10,13], function(){
15125                     if(dlg.isVisible() && opt && opt.buttons){
15126                         if(opt.buttons.ok){
15127                             handleButton("ok");
15128                         }else if(opt.buttons.yes){
15129                             handleButton("yes");
15130                         }
15131                     }
15132                 });
15133                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15134                 textareaEl.enableDisplayMode();
15135                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15136                 progressEl.enableDisplayMode();
15137                 var pf = progressEl.dom.firstChild;
15138                 if (pf) {
15139                     pp = Roo.get(pf.firstChild);
15140                     pp.setHeight(pf.offsetHeight);
15141                 }
15142                 
15143             }
15144             return dlg;
15145         },
15146
15147         /**
15148          * Updates the message box body text
15149          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15150          * the XHTML-compliant non-breaking space character '&amp;#160;')
15151          * @return {Roo.MessageBox} This message box
15152          */
15153         updateText : function(text){
15154             if(!dlg.isVisible() && !opt.width){
15155                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15156             }
15157             msgEl.innerHTML = text || '&#160;';
15158       
15159             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15160             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15161             var w = Math.max(
15162                     Math.min(opt.width || cw , this.maxWidth), 
15163                     Math.max(opt.minWidth || this.minWidth, bwidth)
15164             );
15165             if(opt.prompt){
15166                 activeTextEl.setWidth(w);
15167             }
15168             if(dlg.isVisible()){
15169                 dlg.fixedcenter = false;
15170             }
15171             // to big, make it scroll. = But as usual stupid IE does not support
15172             // !important..
15173             
15174             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15175                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15176                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15177             } else {
15178                 bodyEl.dom.style.height = '';
15179                 bodyEl.dom.style.overflowY = '';
15180             }
15181             if (cw > w) {
15182                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15183             } else {
15184                 bodyEl.dom.style.overflowX = '';
15185             }
15186             
15187             dlg.setContentSize(w, bodyEl.getHeight());
15188             if(dlg.isVisible()){
15189                 dlg.fixedcenter = true;
15190             }
15191             return this;
15192         },
15193
15194         /**
15195          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15196          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15197          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15198          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15199          * @return {Roo.MessageBox} This message box
15200          */
15201         updateProgress : function(value, text){
15202             if(text){
15203                 this.updateText(text);
15204             }
15205             if (pp) { // weird bug on my firefox - for some reason this is not defined
15206                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15207             }
15208             return this;
15209         },        
15210
15211         /**
15212          * Returns true if the message box is currently displayed
15213          * @return {Boolean} True if the message box is visible, else false
15214          */
15215         isVisible : function(){
15216             return dlg && dlg.isVisible();  
15217         },
15218
15219         /**
15220          * Hides the message box if it is displayed
15221          */
15222         hide : function(){
15223             if(this.isVisible()){
15224                 dlg.hide();
15225             }  
15226         },
15227
15228         /**
15229          * Displays a new message box, or reinitializes an existing message box, based on the config options
15230          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15231          * The following config object properties are supported:
15232          * <pre>
15233 Property    Type             Description
15234 ----------  ---------------  ------------------------------------------------------------------------------------
15235 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15236                                    closes (defaults to undefined)
15237 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15238                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15239 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15240                                    progress and wait dialogs will ignore this property and always hide the
15241                                    close button as they can only be closed programmatically.
15242 cls               String           A custom CSS class to apply to the message box element
15243 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15244                                    displayed (defaults to 75)
15245 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15246                                    function will be btn (the name of the button that was clicked, if applicable,
15247                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15248                                    Progress and wait dialogs will ignore this option since they do not respond to
15249                                    user actions and can only be closed programmatically, so any required function
15250                                    should be called by the same code after it closes the dialog.
15251 icon              String           A CSS class that provides a background image to be used as an icon for
15252                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15253 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15254 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15255 modal             Boolean          False to allow user interaction with the page while the message box is
15256                                    displayed (defaults to true)
15257 msg               String           A string that will replace the existing message box body text (defaults
15258                                    to the XHTML-compliant non-breaking space character '&#160;')
15259 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15260 progress          Boolean          True to display a progress bar (defaults to false)
15261 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15262 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15263 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15264 title             String           The title text
15265 value             String           The string value to set into the active textbox element if displayed
15266 wait              Boolean          True to display a progress bar (defaults to false)
15267 width             Number           The width of the dialog in pixels
15268 </pre>
15269          *
15270          * Example usage:
15271          * <pre><code>
15272 Roo.Msg.show({
15273    title: 'Address',
15274    msg: 'Please enter your address:',
15275    width: 300,
15276    buttons: Roo.MessageBox.OKCANCEL,
15277    multiline: true,
15278    fn: saveAddress,
15279    animEl: 'addAddressBtn'
15280 });
15281 </code></pre>
15282          * @param {Object} config Configuration options
15283          * @return {Roo.MessageBox} This message box
15284          */
15285         show : function(options)
15286         {
15287             
15288             // this causes nightmares if you show one dialog after another
15289             // especially on callbacks..
15290              
15291             if(this.isVisible()){
15292                 
15293                 this.hide();
15294                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15295                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15296                 Roo.log("New Dialog Message:" +  options.msg )
15297                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15298                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15299                 
15300             }
15301             var d = this.getDialog();
15302             opt = options;
15303             d.setTitle(opt.title || "&#160;");
15304             d.close.setDisplayed(opt.closable !== false);
15305             activeTextEl = textboxEl;
15306             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15307             if(opt.prompt){
15308                 if(opt.multiline){
15309                     textboxEl.hide();
15310                     textareaEl.show();
15311                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15312                         opt.multiline : this.defaultTextHeight);
15313                     activeTextEl = textareaEl;
15314                 }else{
15315                     textboxEl.show();
15316                     textareaEl.hide();
15317                 }
15318             }else{
15319                 textboxEl.hide();
15320                 textareaEl.hide();
15321             }
15322             progressEl.setDisplayed(opt.progress === true);
15323             this.updateProgress(0);
15324             activeTextEl.dom.value = opt.value || "";
15325             if(opt.prompt){
15326                 dlg.setDefaultButton(activeTextEl);
15327             }else{
15328                 var bs = opt.buttons;
15329                 var db = null;
15330                 if(bs && bs.ok){
15331                     db = buttons["ok"];
15332                 }else if(bs && bs.yes){
15333                     db = buttons["yes"];
15334                 }
15335                 dlg.setDefaultButton(db);
15336             }
15337             bwidth = updateButtons(opt.buttons);
15338             this.updateText(opt.msg);
15339             if(opt.cls){
15340                 d.el.addClass(opt.cls);
15341             }
15342             d.proxyDrag = opt.proxyDrag === true;
15343             d.modal = opt.modal !== false;
15344             d.mask = opt.modal !== false ? mask : false;
15345             if(!d.isVisible()){
15346                 // force it to the end of the z-index stack so it gets a cursor in FF
15347                 document.body.appendChild(dlg.el.dom);
15348                 d.animateTarget = null;
15349                 d.show(options.animEl);
15350             }
15351             return this;
15352         },
15353
15354         /**
15355          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15356          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15357          * and closing the message box when the process is complete.
15358          * @param {String} title The title bar text
15359          * @param {String} msg The message box body text
15360          * @return {Roo.MessageBox} This message box
15361          */
15362         progress : function(title, msg){
15363             this.show({
15364                 title : title,
15365                 msg : msg,
15366                 buttons: false,
15367                 progress:true,
15368                 closable:false,
15369                 minWidth: this.minProgressWidth,
15370                 modal : true
15371             });
15372             return this;
15373         },
15374
15375         /**
15376          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15377          * If a callback function is passed it will be called after the user clicks the button, and the
15378          * id of the button that was clicked will be passed as the only parameter to the callback
15379          * (could also be the top-right close button).
15380          * @param {String} title The title bar text
15381          * @param {String} msg The message box body text
15382          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15383          * @param {Object} scope (optional) The scope of the callback function
15384          * @return {Roo.MessageBox} This message box
15385          */
15386         alert : function(title, msg, fn, scope){
15387             this.show({
15388                 title : title,
15389                 msg : msg,
15390                 buttons: this.OK,
15391                 fn: fn,
15392                 scope : scope,
15393                 modal : true
15394             });
15395             return this;
15396         },
15397
15398         /**
15399          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15400          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15401          * You are responsible for closing the message box when the process is complete.
15402          * @param {String} msg The message box body text
15403          * @param {String} title (optional) The title bar text
15404          * @return {Roo.MessageBox} This message box
15405          */
15406         wait : function(msg, title){
15407             this.show({
15408                 title : title,
15409                 msg : msg,
15410                 buttons: false,
15411                 closable:false,
15412                 progress:true,
15413                 modal:true,
15414                 width:300,
15415                 wait:true
15416             });
15417             waitTimer = Roo.TaskMgr.start({
15418                 run: function(i){
15419                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15420                 },
15421                 interval: 1000
15422             });
15423             return this;
15424         },
15425
15426         /**
15427          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15428          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15429          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15430          * @param {String} title The title bar text
15431          * @param {String} msg The message box body text
15432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15433          * @param {Object} scope (optional) The scope of the callback function
15434          * @return {Roo.MessageBox} This message box
15435          */
15436         confirm : function(title, msg, fn, scope){
15437             this.show({
15438                 title : title,
15439                 msg : msg,
15440                 buttons: this.YESNO,
15441                 fn: fn,
15442                 scope : scope,
15443                 modal : true
15444             });
15445             return this;
15446         },
15447
15448         /**
15449          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15450          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15451          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15452          * (could also be the top-right close button) and the text that was entered will be passed as the two
15453          * parameters to the callback.
15454          * @param {String} title The title bar text
15455          * @param {String} msg The message box body text
15456          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15457          * @param {Object} scope (optional) The scope of the callback function
15458          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15459          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15460          * @return {Roo.MessageBox} This message box
15461          */
15462         prompt : function(title, msg, fn, scope, multiline){
15463             this.show({
15464                 title : title,
15465                 msg : msg,
15466                 buttons: this.OKCANCEL,
15467                 fn: fn,
15468                 minWidth:250,
15469                 scope : scope,
15470                 prompt:true,
15471                 multiline: multiline,
15472                 modal : true
15473             });
15474             return this;
15475         },
15476
15477         /**
15478          * Button config that displays a single OK button
15479          * @type Object
15480          */
15481         OK : {ok:true},
15482         /**
15483          * Button config that displays Yes and No buttons
15484          * @type Object
15485          */
15486         YESNO : {yes:true, no:true},
15487         /**
15488          * Button config that displays OK and Cancel buttons
15489          * @type Object
15490          */
15491         OKCANCEL : {ok:true, cancel:true},
15492         /**
15493          * Button config that displays Yes, No and Cancel buttons
15494          * @type Object
15495          */
15496         YESNOCANCEL : {yes:true, no:true, cancel:true},
15497
15498         /**
15499          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15500          * @type Number
15501          */
15502         defaultTextHeight : 75,
15503         /**
15504          * The maximum width in pixels of the message box (defaults to 600)
15505          * @type Number
15506          */
15507         maxWidth : 600,
15508         /**
15509          * The minimum width in pixels of the message box (defaults to 100)
15510          * @type Number
15511          */
15512         minWidth : 100,
15513         /**
15514          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15515          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15516          * @type Number
15517          */
15518         minProgressWidth : 250,
15519         /**
15520          * An object containing the default button text strings that can be overriden for localized language support.
15521          * Supported properties are: ok, cancel, yes and no.
15522          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15523          * @type Object
15524          */
15525         buttonText : {
15526             ok : "OK",
15527             cancel : "Cancel",
15528             yes : "Yes",
15529             no : "No"
15530         }
15531     };
15532 }();
15533
15534 /**
15535  * Shorthand for {@link Roo.MessageBox}
15536  */
15537 Roo.Msg = Roo.MessageBox;/*
15538  * Based on:
15539  * Ext JS Library 1.1.1
15540  * Copyright(c) 2006-2007, Ext JS, LLC.
15541  *
15542  * Originally Released Under LGPL - original licence link has changed is not relivant.
15543  *
15544  * Fork - LGPL
15545  * <script type="text/javascript">
15546  */
15547 /**
15548  * @class Roo.QuickTips
15549  * Provides attractive and customizable tooltips for any element.
15550  * @singleton
15551  */
15552 Roo.QuickTips = function(){
15553     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15554     var ce, bd, xy, dd;
15555     var visible = false, disabled = true, inited = false;
15556     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15557     
15558     var onOver = function(e){
15559         if(disabled){
15560             return;
15561         }
15562         var t = e.getTarget();
15563         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15564             return;
15565         }
15566         if(ce && t == ce.el){
15567             clearTimeout(hideProc);
15568             return;
15569         }
15570         if(t && tagEls[t.id]){
15571             tagEls[t.id].el = t;
15572             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15573             return;
15574         }
15575         var ttp, et = Roo.fly(t);
15576         var ns = cfg.namespace;
15577         if(tm.interceptTitles && t.title){
15578             ttp = t.title;
15579             t.qtip = ttp;
15580             t.removeAttribute("title");
15581             e.preventDefault();
15582         }else{
15583             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
15584         }
15585         if(ttp){
15586             showProc = show.defer(tm.showDelay, tm, [{
15587                 el: t, 
15588                 text: ttp, 
15589                 width: et.getAttributeNS(ns, cfg.width),
15590                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15591                 title: et.getAttributeNS(ns, cfg.title),
15592                     cls: et.getAttributeNS(ns, cfg.cls)
15593             }]);
15594         }
15595     };
15596     
15597     var onOut = function(e){
15598         clearTimeout(showProc);
15599         var t = e.getTarget();
15600         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15601             hideProc = setTimeout(hide, tm.hideDelay);
15602         }
15603     };
15604     
15605     var onMove = function(e){
15606         if(disabled){
15607             return;
15608         }
15609         xy = e.getXY();
15610         xy[1] += 18;
15611         if(tm.trackMouse && ce){
15612             el.setXY(xy);
15613         }
15614     };
15615     
15616     var onDown = function(e){
15617         clearTimeout(showProc);
15618         clearTimeout(hideProc);
15619         if(!e.within(el)){
15620             if(tm.hideOnClick){
15621                 hide();
15622                 tm.disable();
15623                 tm.enable.defer(100, tm);
15624             }
15625         }
15626     };
15627     
15628     var getPad = function(){
15629         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15630     };
15631
15632     var show = function(o){
15633         if(disabled){
15634             return;
15635         }
15636         clearTimeout(dismissProc);
15637         ce = o;
15638         if(removeCls){ // in case manually hidden
15639             el.removeClass(removeCls);
15640             removeCls = null;
15641         }
15642         if(ce.cls){
15643             el.addClass(ce.cls);
15644             removeCls = ce.cls;
15645         }
15646         if(ce.title){
15647             tipTitle.update(ce.title);
15648             tipTitle.show();
15649         }else{
15650             tipTitle.update('');
15651             tipTitle.hide();
15652         }
15653         el.dom.style.width  = tm.maxWidth+'px';
15654         //tipBody.dom.style.width = '';
15655         tipBodyText.update(o.text);
15656         var p = getPad(), w = ce.width;
15657         if(!w){
15658             var td = tipBodyText.dom;
15659             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15660             if(aw > tm.maxWidth){
15661                 w = tm.maxWidth;
15662             }else if(aw < tm.minWidth){
15663                 w = tm.minWidth;
15664             }else{
15665                 w = aw;
15666             }
15667         }
15668         //tipBody.setWidth(w);
15669         el.setWidth(parseInt(w, 10) + p);
15670         if(ce.autoHide === false){
15671             close.setDisplayed(true);
15672             if(dd){
15673                 dd.unlock();
15674             }
15675         }else{
15676             close.setDisplayed(false);
15677             if(dd){
15678                 dd.lock();
15679             }
15680         }
15681         if(xy){
15682             el.avoidY = xy[1]-18;
15683             el.setXY(xy);
15684         }
15685         if(tm.animate){
15686             el.setOpacity(.1);
15687             el.setStyle("visibility", "visible");
15688             el.fadeIn({callback: afterShow});
15689         }else{
15690             afterShow();
15691         }
15692     };
15693     
15694     var afterShow = function(){
15695         if(ce){
15696             el.show();
15697             esc.enable();
15698             if(tm.autoDismiss && ce.autoHide !== false){
15699                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15700             }
15701         }
15702     };
15703     
15704     var hide = function(noanim){
15705         clearTimeout(dismissProc);
15706         clearTimeout(hideProc);
15707         ce = null;
15708         if(el.isVisible()){
15709             esc.disable();
15710             if(noanim !== true && tm.animate){
15711                 el.fadeOut({callback: afterHide});
15712             }else{
15713                 afterHide();
15714             } 
15715         }
15716     };
15717     
15718     var afterHide = function(){
15719         el.hide();
15720         if(removeCls){
15721             el.removeClass(removeCls);
15722             removeCls = null;
15723         }
15724     };
15725     
15726     return {
15727         /**
15728         * @cfg {Number} minWidth
15729         * The minimum width of the quick tip (defaults to 40)
15730         */
15731        minWidth : 40,
15732         /**
15733         * @cfg {Number} maxWidth
15734         * The maximum width of the quick tip (defaults to 300)
15735         */
15736        maxWidth : 300,
15737         /**
15738         * @cfg {Boolean} interceptTitles
15739         * True to automatically use the element's DOM title value if available (defaults to false)
15740         */
15741        interceptTitles : false,
15742         /**
15743         * @cfg {Boolean} trackMouse
15744         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15745         */
15746        trackMouse : false,
15747         /**
15748         * @cfg {Boolean} hideOnClick
15749         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15750         */
15751        hideOnClick : true,
15752         /**
15753         * @cfg {Number} showDelay
15754         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15755         */
15756        showDelay : 500,
15757         /**
15758         * @cfg {Number} hideDelay
15759         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15760         */
15761        hideDelay : 200,
15762         /**
15763         * @cfg {Boolean} autoHide
15764         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15765         * Used in conjunction with hideDelay.
15766         */
15767        autoHide : true,
15768         /**
15769         * @cfg {Boolean}
15770         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15771         * (defaults to true).  Used in conjunction with autoDismissDelay.
15772         */
15773        autoDismiss : true,
15774         /**
15775         * @cfg {Number}
15776         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15777         */
15778        autoDismissDelay : 5000,
15779        /**
15780         * @cfg {Boolean} animate
15781         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15782         */
15783        animate : false,
15784
15785        /**
15786         * @cfg {String} title
15787         * Title text to display (defaults to '').  This can be any valid HTML markup.
15788         */
15789         title: '',
15790        /**
15791         * @cfg {String} text
15792         * Body text to display (defaults to '').  This can be any valid HTML markup.
15793         */
15794         text : '',
15795        /**
15796         * @cfg {String} cls
15797         * A CSS class to apply to the base quick tip element (defaults to '').
15798         */
15799         cls : '',
15800        /**
15801         * @cfg {Number} width
15802         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15803         * minWidth or maxWidth.
15804         */
15805         width : null,
15806
15807     /**
15808      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15809      * or display QuickTips in a page.
15810      */
15811        init : function(){
15812           tm = Roo.QuickTips;
15813           cfg = tm.tagConfig;
15814           if(!inited){
15815               if(!Roo.isReady){ // allow calling of init() before onReady
15816                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15817                   return;
15818               }
15819               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15820               el.fxDefaults = {stopFx: true};
15821               // maximum custom styling
15822               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
15823               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
15824               tipTitle = el.child('h3');
15825               tipTitle.enableDisplayMode("block");
15826               tipBody = el.child('div.x-tip-bd');
15827               tipBodyText = el.child('div.x-tip-bd-inner');
15828               //bdLeft = el.child('div.x-tip-bd-left');
15829               //bdRight = el.child('div.x-tip-bd-right');
15830               close = el.child('div.x-tip-close');
15831               close.enableDisplayMode("block");
15832               close.on("click", hide);
15833               var d = Roo.get(document);
15834               d.on("mousedown", onDown);
15835               d.on("mouseover", onOver);
15836               d.on("mouseout", onOut);
15837               d.on("mousemove", onMove);
15838               esc = d.addKeyListener(27, hide);
15839               esc.disable();
15840               if(Roo.dd.DD){
15841                   dd = el.initDD("default", null, {
15842                       onDrag : function(){
15843                           el.sync();  
15844                       }
15845                   });
15846                   dd.setHandleElId(tipTitle.id);
15847                   dd.lock();
15848               }
15849               inited = true;
15850           }
15851           this.enable(); 
15852        },
15853
15854     /**
15855      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15856      * are supported:
15857      * <pre>
15858 Property    Type                   Description
15859 ----------  ---------------------  ------------------------------------------------------------------------
15860 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15861      * </ul>
15862      * @param {Object} config The config object
15863      */
15864        register : function(config){
15865            var cs = config instanceof Array ? config : arguments;
15866            for(var i = 0, len = cs.length; i < len; i++) {
15867                var c = cs[i];
15868                var target = c.target;
15869                if(target){
15870                    if(target instanceof Array){
15871                        for(var j = 0, jlen = target.length; j < jlen; j++){
15872                            tagEls[target[j]] = c;
15873                        }
15874                    }else{
15875                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15876                    }
15877                }
15878            }
15879        },
15880
15881     /**
15882      * Removes this quick tip from its element and destroys it.
15883      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15884      */
15885        unregister : function(el){
15886            delete tagEls[Roo.id(el)];
15887        },
15888
15889     /**
15890      * Enable this quick tip.
15891      */
15892        enable : function(){
15893            if(inited && disabled){
15894                locks.pop();
15895                if(locks.length < 1){
15896                    disabled = false;
15897                }
15898            }
15899        },
15900
15901     /**
15902      * Disable this quick tip.
15903      */
15904        disable : function(){
15905           disabled = true;
15906           clearTimeout(showProc);
15907           clearTimeout(hideProc);
15908           clearTimeout(dismissProc);
15909           if(ce){
15910               hide(true);
15911           }
15912           locks.push(1);
15913        },
15914
15915     /**
15916      * Returns true if the quick tip is enabled, else false.
15917      */
15918        isEnabled : function(){
15919             return !disabled;
15920        },
15921
15922         // private
15923        tagConfig : {
15924            namespace : "roo", // was ext?? this may break..
15925            alt_namespace : "ext",
15926            attribute : "qtip",
15927            width : "width",
15928            target : "target",
15929            title : "qtitle",
15930            hide : "hide",
15931            cls : "qclass"
15932        }
15933    };
15934 }();
15935
15936 // backwards compat
15937 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15938  * Based on:
15939  * Ext JS Library 1.1.1
15940  * Copyright(c) 2006-2007, Ext JS, LLC.
15941  *
15942  * Originally Released Under LGPL - original licence link has changed is not relivant.
15943  *
15944  * Fork - LGPL
15945  * <script type="text/javascript">
15946  */
15947  
15948
15949 /**
15950  * @class Roo.tree.TreePanel
15951  * @extends Roo.data.Tree
15952
15953  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15954  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15955  * @cfg {Boolean} enableDD true to enable drag and drop
15956  * @cfg {Boolean} enableDrag true to enable just drag
15957  * @cfg {Boolean} enableDrop true to enable just drop
15958  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15959  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15960  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15961  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15962  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15963  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15964  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15965  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15966  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15967  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15968  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15969  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15970  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15971  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15972  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
15973  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
15974  * 
15975  * @constructor
15976  * @param {String/HTMLElement/Element} el The container element
15977  * @param {Object} config
15978  */
15979 Roo.tree.TreePanel = function(el, config){
15980     var root = false;
15981     var loader = false;
15982     if (config.root) {
15983         root = config.root;
15984         delete config.root;
15985     }
15986     if (config.loader) {
15987         loader = config.loader;
15988         delete config.loader;
15989     }
15990     
15991     Roo.apply(this, config);
15992     Roo.tree.TreePanel.superclass.constructor.call(this);
15993     this.el = Roo.get(el);
15994     this.el.addClass('x-tree');
15995     //console.log(root);
15996     if (root) {
15997         this.setRootNode( Roo.factory(root, Roo.tree));
15998     }
15999     if (loader) {
16000         this.loader = Roo.factory(loader, Roo.tree);
16001     }
16002    /**
16003     * Read-only. The id of the container element becomes this TreePanel's id.
16004     */
16005     this.id = this.el.id;
16006     this.addEvents({
16007         /**
16008         * @event beforeload
16009         * Fires before a node is loaded, return false to cancel
16010         * @param {Node} node The node being loaded
16011         */
16012         "beforeload" : true,
16013         /**
16014         * @event load
16015         * Fires when a node is loaded
16016         * @param {Node} node The node that was loaded
16017         */
16018         "load" : true,
16019         /**
16020         * @event textchange
16021         * Fires when the text for a node is changed
16022         * @param {Node} node The node
16023         * @param {String} text The new text
16024         * @param {String} oldText The old text
16025         */
16026         "textchange" : true,
16027         /**
16028         * @event beforeexpand
16029         * Fires before a node is expanded, return false to cancel.
16030         * @param {Node} node The node
16031         * @param {Boolean} deep
16032         * @param {Boolean} anim
16033         */
16034         "beforeexpand" : true,
16035         /**
16036         * @event beforecollapse
16037         * Fires before a node is collapsed, return false to cancel.
16038         * @param {Node} node The node
16039         * @param {Boolean} deep
16040         * @param {Boolean} anim
16041         */
16042         "beforecollapse" : true,
16043         /**
16044         * @event expand
16045         * Fires when a node is expanded
16046         * @param {Node} node The node
16047         */
16048         "expand" : true,
16049         /**
16050         * @event disabledchange
16051         * Fires when the disabled status of a node changes
16052         * @param {Node} node The node
16053         * @param {Boolean} disabled
16054         */
16055         "disabledchange" : true,
16056         /**
16057         * @event collapse
16058         * Fires when a node is collapsed
16059         * @param {Node} node The node
16060         */
16061         "collapse" : true,
16062         /**
16063         * @event beforeclick
16064         * Fires before click processing on a node. Return false to cancel the default action.
16065         * @param {Node} node The node
16066         * @param {Roo.EventObject} e The event object
16067         */
16068         "beforeclick":true,
16069         /**
16070         * @event checkchange
16071         * Fires when a node with a checkbox's checked property changes
16072         * @param {Node} this This node
16073         * @param {Boolean} checked
16074         */
16075         "checkchange":true,
16076         /**
16077         * @event click
16078         * Fires when a node is clicked
16079         * @param {Node} node The node
16080         * @param {Roo.EventObject} e The event object
16081         */
16082         "click":true,
16083         /**
16084         * @event dblclick
16085         * Fires when a node is double clicked
16086         * @param {Node} node The node
16087         * @param {Roo.EventObject} e The event object
16088         */
16089         "dblclick":true,
16090         /**
16091         * @event contextmenu
16092         * Fires when a node is right clicked
16093         * @param {Node} node The node
16094         * @param {Roo.EventObject} e The event object
16095         */
16096         "contextmenu":true,
16097         /**
16098         * @event beforechildrenrendered
16099         * Fires right before the child nodes for a node are rendered
16100         * @param {Node} node The node
16101         */
16102         "beforechildrenrendered":true,
16103         /**
16104         * @event startdrag
16105         * Fires when a node starts being dragged
16106         * @param {Roo.tree.TreePanel} this
16107         * @param {Roo.tree.TreeNode} node
16108         * @param {event} e The raw browser event
16109         */ 
16110        "startdrag" : true,
16111        /**
16112         * @event enddrag
16113         * Fires when a drag operation is complete
16114         * @param {Roo.tree.TreePanel} this
16115         * @param {Roo.tree.TreeNode} node
16116         * @param {event} e The raw browser event
16117         */
16118        "enddrag" : true,
16119        /**
16120         * @event dragdrop
16121         * Fires when a dragged node is dropped on a valid DD target
16122         * @param {Roo.tree.TreePanel} this
16123         * @param {Roo.tree.TreeNode} node
16124         * @param {DD} dd The dd it was dropped on
16125         * @param {event} e The raw browser event
16126         */
16127        "dragdrop" : true,
16128        /**
16129         * @event beforenodedrop
16130         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16131         * passed to handlers has the following properties:<br />
16132         * <ul style="padding:5px;padding-left:16px;">
16133         * <li>tree - The TreePanel</li>
16134         * <li>target - The node being targeted for the drop</li>
16135         * <li>data - The drag data from the drag source</li>
16136         * <li>point - The point of the drop - append, above or below</li>
16137         * <li>source - The drag source</li>
16138         * <li>rawEvent - Raw mouse event</li>
16139         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16140         * to be inserted by setting them on this object.</li>
16141         * <li>cancel - Set this to true to cancel the drop.</li>
16142         * </ul>
16143         * @param {Object} dropEvent
16144         */
16145        "beforenodedrop" : true,
16146        /**
16147         * @event nodedrop
16148         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16149         * passed to handlers has the following properties:<br />
16150         * <ul style="padding:5px;padding-left:16px;">
16151         * <li>tree - The TreePanel</li>
16152         * <li>target - The node being targeted for the drop</li>
16153         * <li>data - The drag data from the drag source</li>
16154         * <li>point - The point of the drop - append, above or below</li>
16155         * <li>source - The drag source</li>
16156         * <li>rawEvent - Raw mouse event</li>
16157         * <li>dropNode - Dropped node(s).</li>
16158         * </ul>
16159         * @param {Object} dropEvent
16160         */
16161        "nodedrop" : true,
16162         /**
16163         * @event nodedragover
16164         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16165         * passed to handlers has the following properties:<br />
16166         * <ul style="padding:5px;padding-left:16px;">
16167         * <li>tree - The TreePanel</li>
16168         * <li>target - The node being targeted for the drop</li>
16169         * <li>data - The drag data from the drag source</li>
16170         * <li>point - The point of the drop - append, above or below</li>
16171         * <li>source - The drag source</li>
16172         * <li>rawEvent - Raw mouse event</li>
16173         * <li>dropNode - Drop node(s) provided by the source.</li>
16174         * <li>cancel - Set this to true to signal drop not allowed.</li>
16175         * </ul>
16176         * @param {Object} dragOverEvent
16177         */
16178        "nodedragover" : true
16179         
16180     });
16181     if(this.singleExpand){
16182        this.on("beforeexpand", this.restrictExpand, this);
16183     }
16184     if (this.editor) {
16185         this.editor.tree = this;
16186         this.editor = Roo.factory(this.editor, Roo.tree);
16187     }
16188     
16189     if (this.selModel) {
16190         this.selModel = Roo.factory(this.selModel, Roo.tree);
16191     }
16192    
16193 };
16194 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16195     rootVisible : true,
16196     animate: Roo.enableFx,
16197     lines : true,
16198     enableDD : false,
16199     hlDrop : Roo.enableFx,
16200   
16201     renderer: false,
16202     
16203     rendererTip: false,
16204     // private
16205     restrictExpand : function(node){
16206         var p = node.parentNode;
16207         if(p){
16208             if(p.expandedChild && p.expandedChild.parentNode == p){
16209                 p.expandedChild.collapse();
16210             }
16211             p.expandedChild = node;
16212         }
16213     },
16214
16215     // private override
16216     setRootNode : function(node){
16217         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16218         if(!this.rootVisible){
16219             node.ui = new Roo.tree.RootTreeNodeUI(node);
16220         }
16221         return node;
16222     },
16223
16224     /**
16225      * Returns the container element for this TreePanel
16226      */
16227     getEl : function(){
16228         return this.el;
16229     },
16230
16231     /**
16232      * Returns the default TreeLoader for this TreePanel
16233      */
16234     getLoader : function(){
16235         return this.loader;
16236     },
16237
16238     /**
16239      * Expand all nodes
16240      */
16241     expandAll : function(){
16242         this.root.expand(true);
16243     },
16244
16245     /**
16246      * Collapse all nodes
16247      */
16248     collapseAll : function(){
16249         this.root.collapse(true);
16250     },
16251
16252     /**
16253      * Returns the selection model used by this TreePanel
16254      */
16255     getSelectionModel : function(){
16256         if(!this.selModel){
16257             this.selModel = new Roo.tree.DefaultSelectionModel();
16258         }
16259         return this.selModel;
16260     },
16261
16262     /**
16263      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16264      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16265      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16266      * @return {Array}
16267      */
16268     getChecked : function(a, startNode){
16269         startNode = startNode || this.root;
16270         var r = [];
16271         var f = function(){
16272             if(this.attributes.checked){
16273                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16274             }
16275         }
16276         startNode.cascade(f);
16277         return r;
16278     },
16279
16280     /**
16281      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16282      * @param {String} path
16283      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16284      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16285      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16286      */
16287     expandPath : function(path, attr, callback){
16288         attr = attr || "id";
16289         var keys = path.split(this.pathSeparator);
16290         var curNode = this.root;
16291         if(curNode.attributes[attr] != keys[1]){ // invalid root
16292             if(callback){
16293                 callback(false, null);
16294             }
16295             return;
16296         }
16297         var index = 1;
16298         var f = function(){
16299             if(++index == keys.length){
16300                 if(callback){
16301                     callback(true, curNode);
16302                 }
16303                 return;
16304             }
16305             var c = curNode.findChild(attr, keys[index]);
16306             if(!c){
16307                 if(callback){
16308                     callback(false, curNode);
16309                 }
16310                 return;
16311             }
16312             curNode = c;
16313             c.expand(false, false, f);
16314         };
16315         curNode.expand(false, false, f);
16316     },
16317
16318     /**
16319      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16320      * @param {String} path
16321      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16322      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16323      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16324      */
16325     selectPath : function(path, attr, callback){
16326         attr = attr || "id";
16327         var keys = path.split(this.pathSeparator);
16328         var v = keys.pop();
16329         if(keys.length > 0){
16330             var f = function(success, node){
16331                 if(success && node){
16332                     var n = node.findChild(attr, v);
16333                     if(n){
16334                         n.select();
16335                         if(callback){
16336                             callback(true, n);
16337                         }
16338                     }else if(callback){
16339                         callback(false, n);
16340                     }
16341                 }else{
16342                     if(callback){
16343                         callback(false, n);
16344                     }
16345                 }
16346             };
16347             this.expandPath(keys.join(this.pathSeparator), attr, f);
16348         }else{
16349             this.root.select();
16350             if(callback){
16351                 callback(true, this.root);
16352             }
16353         }
16354     },
16355
16356     getTreeEl : function(){
16357         return this.el;
16358     },
16359
16360     /**
16361      * Trigger rendering of this TreePanel
16362      */
16363     render : function(){
16364         if (this.innerCt) {
16365             return this; // stop it rendering more than once!!
16366         }
16367         
16368         this.innerCt = this.el.createChild({tag:"ul",
16369                cls:"x-tree-root-ct " +
16370                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16371
16372         if(this.containerScroll){
16373             Roo.dd.ScrollManager.register(this.el);
16374         }
16375         if((this.enableDD || this.enableDrop) && !this.dropZone){
16376            /**
16377             * The dropZone used by this tree if drop is enabled
16378             * @type Roo.tree.TreeDropZone
16379             */
16380              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16381                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16382            });
16383         }
16384         if((this.enableDD || this.enableDrag) && !this.dragZone){
16385            /**
16386             * The dragZone used by this tree if drag is enabled
16387             * @type Roo.tree.TreeDragZone
16388             */
16389             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16390                ddGroup: this.ddGroup || "TreeDD",
16391                scroll: this.ddScroll
16392            });
16393         }
16394         this.getSelectionModel().init(this);
16395         if (!this.root) {
16396             Roo.log("ROOT not set in tree");
16397             return this;
16398         }
16399         this.root.render();
16400         if(!this.rootVisible){
16401             this.root.renderChildren();
16402         }
16403         return this;
16404     }
16405 });/*
16406  * Based on:
16407  * Ext JS Library 1.1.1
16408  * Copyright(c) 2006-2007, Ext JS, LLC.
16409  *
16410  * Originally Released Under LGPL - original licence link has changed is not relivant.
16411  *
16412  * Fork - LGPL
16413  * <script type="text/javascript">
16414  */
16415  
16416
16417 /**
16418  * @class Roo.tree.DefaultSelectionModel
16419  * @extends Roo.util.Observable
16420  * The default single selection for a TreePanel.
16421  * @param {Object} cfg Configuration
16422  */
16423 Roo.tree.DefaultSelectionModel = function(cfg){
16424    this.selNode = null;
16425    
16426    
16427    
16428    this.addEvents({
16429        /**
16430         * @event selectionchange
16431         * Fires when the selected node changes
16432         * @param {DefaultSelectionModel} this
16433         * @param {TreeNode} node the new selection
16434         */
16435        "selectionchange" : true,
16436
16437        /**
16438         * @event beforeselect
16439         * Fires before the selected node changes, return false to cancel the change
16440         * @param {DefaultSelectionModel} this
16441         * @param {TreeNode} node the new selection
16442         * @param {TreeNode} node the old selection
16443         */
16444        "beforeselect" : true
16445    });
16446    
16447     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16448 };
16449
16450 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16451     init : function(tree){
16452         this.tree = tree;
16453         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16454         tree.on("click", this.onNodeClick, this);
16455     },
16456     
16457     onNodeClick : function(node, e){
16458         if (e.ctrlKey && this.selNode == node)  {
16459             this.unselect(node);
16460             return;
16461         }
16462         this.select(node);
16463     },
16464     
16465     /**
16466      * Select a node.
16467      * @param {TreeNode} node The node to select
16468      * @return {TreeNode} The selected node
16469      */
16470     select : function(node){
16471         var last = this.selNode;
16472         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16473             if(last){
16474                 last.ui.onSelectedChange(false);
16475             }
16476             this.selNode = node;
16477             node.ui.onSelectedChange(true);
16478             this.fireEvent("selectionchange", this, node, last);
16479         }
16480         return node;
16481     },
16482     
16483     /**
16484      * Deselect a node.
16485      * @param {TreeNode} node The node to unselect
16486      */
16487     unselect : function(node){
16488         if(this.selNode == node){
16489             this.clearSelections();
16490         }    
16491     },
16492     
16493     /**
16494      * Clear all selections
16495      */
16496     clearSelections : function(){
16497         var n = this.selNode;
16498         if(n){
16499             n.ui.onSelectedChange(false);
16500             this.selNode = null;
16501             this.fireEvent("selectionchange", this, null);
16502         }
16503         return n;
16504     },
16505     
16506     /**
16507      * Get the selected node
16508      * @return {TreeNode} The selected node
16509      */
16510     getSelectedNode : function(){
16511         return this.selNode;    
16512     },
16513     
16514     /**
16515      * Returns true if the node is selected
16516      * @param {TreeNode} node The node to check
16517      * @return {Boolean}
16518      */
16519     isSelected : function(node){
16520         return this.selNode == node;  
16521     },
16522
16523     /**
16524      * Selects the node above the selected node in the tree, intelligently walking the nodes
16525      * @return TreeNode The new selection
16526      */
16527     selectPrevious : function(){
16528         var s = this.selNode || this.lastSelNode;
16529         if(!s){
16530             return null;
16531         }
16532         var ps = s.previousSibling;
16533         if(ps){
16534             if(!ps.isExpanded() || ps.childNodes.length < 1){
16535                 return this.select(ps);
16536             } else{
16537                 var lc = ps.lastChild;
16538                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16539                     lc = lc.lastChild;
16540                 }
16541                 return this.select(lc);
16542             }
16543         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16544             return this.select(s.parentNode);
16545         }
16546         return null;
16547     },
16548
16549     /**
16550      * Selects the node above the selected node in the tree, intelligently walking the nodes
16551      * @return TreeNode The new selection
16552      */
16553     selectNext : function(){
16554         var s = this.selNode || this.lastSelNode;
16555         if(!s){
16556             return null;
16557         }
16558         if(s.firstChild && s.isExpanded()){
16559              return this.select(s.firstChild);
16560          }else if(s.nextSibling){
16561              return this.select(s.nextSibling);
16562          }else if(s.parentNode){
16563             var newS = null;
16564             s.parentNode.bubble(function(){
16565                 if(this.nextSibling){
16566                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16567                     return false;
16568                 }
16569             });
16570             return newS;
16571          }
16572         return null;
16573     },
16574
16575     onKeyDown : function(e){
16576         var s = this.selNode || this.lastSelNode;
16577         // undesirable, but required
16578         var sm = this;
16579         if(!s){
16580             return;
16581         }
16582         var k = e.getKey();
16583         switch(k){
16584              case e.DOWN:
16585                  e.stopEvent();
16586                  this.selectNext();
16587              break;
16588              case e.UP:
16589                  e.stopEvent();
16590                  this.selectPrevious();
16591              break;
16592              case e.RIGHT:
16593                  e.preventDefault();
16594                  if(s.hasChildNodes()){
16595                      if(!s.isExpanded()){
16596                          s.expand();
16597                      }else if(s.firstChild){
16598                          this.select(s.firstChild, e);
16599                      }
16600                  }
16601              break;
16602              case e.LEFT:
16603                  e.preventDefault();
16604                  if(s.hasChildNodes() && s.isExpanded()){
16605                      s.collapse();
16606                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16607                      this.select(s.parentNode, e);
16608                  }
16609              break;
16610         };
16611     }
16612 });
16613
16614 /**
16615  * @class Roo.tree.MultiSelectionModel
16616  * @extends Roo.util.Observable
16617  * Multi selection for a TreePanel.
16618  * @param {Object} cfg Configuration
16619  */
16620 Roo.tree.MultiSelectionModel = function(){
16621    this.selNodes = [];
16622    this.selMap = {};
16623    this.addEvents({
16624        /**
16625         * @event selectionchange
16626         * Fires when the selected nodes change
16627         * @param {MultiSelectionModel} this
16628         * @param {Array} nodes Array of the selected nodes
16629         */
16630        "selectionchange" : true
16631    });
16632    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16633    
16634 };
16635
16636 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16637     init : function(tree){
16638         this.tree = tree;
16639         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16640         tree.on("click", this.onNodeClick, this);
16641     },
16642     
16643     onNodeClick : function(node, e){
16644         this.select(node, e, e.ctrlKey);
16645     },
16646     
16647     /**
16648      * Select a node.
16649      * @param {TreeNode} node The node to select
16650      * @param {EventObject} e (optional) An event associated with the selection
16651      * @param {Boolean} keepExisting True to retain existing selections
16652      * @return {TreeNode} The selected node
16653      */
16654     select : function(node, e, keepExisting){
16655         if(keepExisting !== true){
16656             this.clearSelections(true);
16657         }
16658         if(this.isSelected(node)){
16659             this.lastSelNode = node;
16660             return node;
16661         }
16662         this.selNodes.push(node);
16663         this.selMap[node.id] = node;
16664         this.lastSelNode = node;
16665         node.ui.onSelectedChange(true);
16666         this.fireEvent("selectionchange", this, this.selNodes);
16667         return node;
16668     },
16669     
16670     /**
16671      * Deselect a node.
16672      * @param {TreeNode} node The node to unselect
16673      */
16674     unselect : function(node){
16675         if(this.selMap[node.id]){
16676             node.ui.onSelectedChange(false);
16677             var sn = this.selNodes;
16678             var index = -1;
16679             if(sn.indexOf){
16680                 index = sn.indexOf(node);
16681             }else{
16682                 for(var i = 0, len = sn.length; i < len; i++){
16683                     if(sn[i] == node){
16684                         index = i;
16685                         break;
16686                     }
16687                 }
16688             }
16689             if(index != -1){
16690                 this.selNodes.splice(index, 1);
16691             }
16692             delete this.selMap[node.id];
16693             this.fireEvent("selectionchange", this, this.selNodes);
16694         }
16695     },
16696     
16697     /**
16698      * Clear all selections
16699      */
16700     clearSelections : function(suppressEvent){
16701         var sn = this.selNodes;
16702         if(sn.length > 0){
16703             for(var i = 0, len = sn.length; i < len; i++){
16704                 sn[i].ui.onSelectedChange(false);
16705             }
16706             this.selNodes = [];
16707             this.selMap = {};
16708             if(suppressEvent !== true){
16709                 this.fireEvent("selectionchange", this, this.selNodes);
16710             }
16711         }
16712     },
16713     
16714     /**
16715      * Returns true if the node is selected
16716      * @param {TreeNode} node The node to check
16717      * @return {Boolean}
16718      */
16719     isSelected : function(node){
16720         return this.selMap[node.id] ? true : false;  
16721     },
16722     
16723     /**
16724      * Returns an array of the selected nodes
16725      * @return {Array}
16726      */
16727     getSelectedNodes : function(){
16728         return this.selNodes;    
16729     },
16730
16731     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16732
16733     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16734
16735     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16736 });/*
16737  * Based on:
16738  * Ext JS Library 1.1.1
16739  * Copyright(c) 2006-2007, Ext JS, LLC.
16740  *
16741  * Originally Released Under LGPL - original licence link has changed is not relivant.
16742  *
16743  * Fork - LGPL
16744  * <script type="text/javascript">
16745  */
16746  
16747 /**
16748  * @class Roo.tree.TreeNode
16749  * @extends Roo.data.Node
16750  * @cfg {String} text The text for this node
16751  * @cfg {Boolean} expanded true to start the node expanded
16752  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16753  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16754  * @cfg {Boolean} disabled true to start the node disabled
16755  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16756  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16757  * @cfg {String} cls A css class to be added to the node
16758  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16759  * @cfg {String} href URL of the link used for the node (defaults to #)
16760  * @cfg {String} hrefTarget target frame for the link
16761  * @cfg {String} qtip An Ext QuickTip for the node
16762  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16763  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16764  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16765  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16766  * (defaults to undefined with no checkbox rendered)
16767  * @constructor
16768  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16769  */
16770 Roo.tree.TreeNode = function(attributes){
16771     attributes = attributes || {};
16772     if(typeof attributes == "string"){
16773         attributes = {text: attributes};
16774     }
16775     this.childrenRendered = false;
16776     this.rendered = false;
16777     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16778     this.expanded = attributes.expanded === true;
16779     this.isTarget = attributes.isTarget !== false;
16780     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16781     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16782
16783     /**
16784      * Read-only. The text for this node. To change it use setText().
16785      * @type String
16786      */
16787     this.text = attributes.text;
16788     /**
16789      * True if this node is disabled.
16790      * @type Boolean
16791      */
16792     this.disabled = attributes.disabled === true;
16793
16794     this.addEvents({
16795         /**
16796         * @event textchange
16797         * Fires when the text for this node is changed
16798         * @param {Node} this This node
16799         * @param {String} text The new text
16800         * @param {String} oldText The old text
16801         */
16802         "textchange" : true,
16803         /**
16804         * @event beforeexpand
16805         * Fires before this node is expanded, return false to cancel.
16806         * @param {Node} this This node
16807         * @param {Boolean} deep
16808         * @param {Boolean} anim
16809         */
16810         "beforeexpand" : true,
16811         /**
16812         * @event beforecollapse
16813         * Fires before this node is collapsed, return false to cancel.
16814         * @param {Node} this This node
16815         * @param {Boolean} deep
16816         * @param {Boolean} anim
16817         */
16818         "beforecollapse" : true,
16819         /**
16820         * @event expand
16821         * Fires when this node is expanded
16822         * @param {Node} this This node
16823         */
16824         "expand" : true,
16825         /**
16826         * @event disabledchange
16827         * Fires when the disabled status of this node changes
16828         * @param {Node} this This node
16829         * @param {Boolean} disabled
16830         */
16831         "disabledchange" : true,
16832         /**
16833         * @event collapse
16834         * Fires when this node is collapsed
16835         * @param {Node} this This node
16836         */
16837         "collapse" : true,
16838         /**
16839         * @event beforeclick
16840         * Fires before click processing. Return false to cancel the default action.
16841         * @param {Node} this This node
16842         * @param {Roo.EventObject} e The event object
16843         */
16844         "beforeclick":true,
16845         /**
16846         * @event checkchange
16847         * Fires when a node with a checkbox's checked property changes
16848         * @param {Node} this This node
16849         * @param {Boolean} checked
16850         */
16851         "checkchange":true,
16852         /**
16853         * @event click
16854         * Fires when this node is clicked
16855         * @param {Node} this This node
16856         * @param {Roo.EventObject} e The event object
16857         */
16858         "click":true,
16859         /**
16860         * @event dblclick
16861         * Fires when this node is double clicked
16862         * @param {Node} this This node
16863         * @param {Roo.EventObject} e The event object
16864         */
16865         "dblclick":true,
16866         /**
16867         * @event contextmenu
16868         * Fires when this node is right clicked
16869         * @param {Node} this This node
16870         * @param {Roo.EventObject} e The event object
16871         */
16872         "contextmenu":true,
16873         /**
16874         * @event beforechildrenrendered
16875         * Fires right before the child nodes for this node are rendered
16876         * @param {Node} this This node
16877         */
16878         "beforechildrenrendered":true
16879     });
16880
16881     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16882
16883     /**
16884      * Read-only. The UI for this node
16885      * @type TreeNodeUI
16886      */
16887     this.ui = new uiClass(this);
16888     
16889     // finally support items[]
16890     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16891         return;
16892     }
16893     
16894     
16895     Roo.each(this.attributes.items, function(c) {
16896         this.appendChild(Roo.factory(c,Roo.Tree));
16897     }, this);
16898     delete this.attributes.items;
16899     
16900     
16901     
16902 };
16903 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16904     preventHScroll: true,
16905     /**
16906      * Returns true if this node is expanded
16907      * @return {Boolean}
16908      */
16909     isExpanded : function(){
16910         return this.expanded;
16911     },
16912
16913     /**
16914      * Returns the UI object for this node
16915      * @return {TreeNodeUI}
16916      */
16917     getUI : function(){
16918         return this.ui;
16919     },
16920
16921     // private override
16922     setFirstChild : function(node){
16923         var of = this.firstChild;
16924         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16925         if(this.childrenRendered && of && node != of){
16926             of.renderIndent(true, true);
16927         }
16928         if(this.rendered){
16929             this.renderIndent(true, true);
16930         }
16931     },
16932
16933     // private override
16934     setLastChild : function(node){
16935         var ol = this.lastChild;
16936         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16937         if(this.childrenRendered && ol && node != ol){
16938             ol.renderIndent(true, true);
16939         }
16940         if(this.rendered){
16941             this.renderIndent(true, true);
16942         }
16943     },
16944
16945     // these methods are overridden to provide lazy rendering support
16946     // private override
16947     appendChild : function()
16948     {
16949         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16950         if(node && this.childrenRendered){
16951             node.render();
16952         }
16953         this.ui.updateExpandIcon();
16954         return node;
16955     },
16956
16957     // private override
16958     removeChild : function(node){
16959         this.ownerTree.getSelectionModel().unselect(node);
16960         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16961         // if it's been rendered remove dom node
16962         if(this.childrenRendered){
16963             node.ui.remove();
16964         }
16965         if(this.childNodes.length < 1){
16966             this.collapse(false, false);
16967         }else{
16968             this.ui.updateExpandIcon();
16969         }
16970         if(!this.firstChild) {
16971             this.childrenRendered = false;
16972         }
16973         return node;
16974     },
16975
16976     // private override
16977     insertBefore : function(node, refNode){
16978         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16979         if(newNode && refNode && this.childrenRendered){
16980             node.render();
16981         }
16982         this.ui.updateExpandIcon();
16983         return newNode;
16984     },
16985
16986     /**
16987      * Sets the text for this node
16988      * @param {String} text
16989      */
16990     setText : function(text){
16991         var oldText = this.text;
16992         this.text = text;
16993         this.attributes.text = text;
16994         if(this.rendered){ // event without subscribing
16995             this.ui.onTextChange(this, text, oldText);
16996         }
16997         this.fireEvent("textchange", this, text, oldText);
16998     },
16999
17000     /**
17001      * Triggers selection of this node
17002      */
17003     select : function(){
17004         this.getOwnerTree().getSelectionModel().select(this);
17005     },
17006
17007     /**
17008      * Triggers deselection of this node
17009      */
17010     unselect : function(){
17011         this.getOwnerTree().getSelectionModel().unselect(this);
17012     },
17013
17014     /**
17015      * Returns true if this node is selected
17016      * @return {Boolean}
17017      */
17018     isSelected : function(){
17019         return this.getOwnerTree().getSelectionModel().isSelected(this);
17020     },
17021
17022     /**
17023      * Expand this node.
17024      * @param {Boolean} deep (optional) True to expand all children as well
17025      * @param {Boolean} anim (optional) false to cancel the default animation
17026      * @param {Function} callback (optional) A callback to be called when
17027      * expanding this node completes (does not wait for deep expand to complete).
17028      * Called with 1 parameter, this node.
17029      */
17030     expand : function(deep, anim, callback){
17031         if(!this.expanded){
17032             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17033                 return;
17034             }
17035             if(!this.childrenRendered){
17036                 this.renderChildren();
17037             }
17038             this.expanded = true;
17039             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17040                 this.ui.animExpand(function(){
17041                     this.fireEvent("expand", this);
17042                     if(typeof callback == "function"){
17043                         callback(this);
17044                     }
17045                     if(deep === true){
17046                         this.expandChildNodes(true);
17047                     }
17048                 }.createDelegate(this));
17049                 return;
17050             }else{
17051                 this.ui.expand();
17052                 this.fireEvent("expand", this);
17053                 if(typeof callback == "function"){
17054                     callback(this);
17055                 }
17056             }
17057         }else{
17058            if(typeof callback == "function"){
17059                callback(this);
17060            }
17061         }
17062         if(deep === true){
17063             this.expandChildNodes(true);
17064         }
17065     },
17066
17067     isHiddenRoot : function(){
17068         return this.isRoot && !this.getOwnerTree().rootVisible;
17069     },
17070
17071     /**
17072      * Collapse this node.
17073      * @param {Boolean} deep (optional) True to collapse all children as well
17074      * @param {Boolean} anim (optional) false to cancel the default animation
17075      */
17076     collapse : function(deep, anim){
17077         if(this.expanded && !this.isHiddenRoot()){
17078             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17079                 return;
17080             }
17081             this.expanded = false;
17082             if((this.getOwnerTree().animate && anim !== false) || anim){
17083                 this.ui.animCollapse(function(){
17084                     this.fireEvent("collapse", this);
17085                     if(deep === true){
17086                         this.collapseChildNodes(true);
17087                     }
17088                 }.createDelegate(this));
17089                 return;
17090             }else{
17091                 this.ui.collapse();
17092                 this.fireEvent("collapse", this);
17093             }
17094         }
17095         if(deep === true){
17096             var cs = this.childNodes;
17097             for(var i = 0, len = cs.length; i < len; i++) {
17098                 cs[i].collapse(true, false);
17099             }
17100         }
17101     },
17102
17103     // private
17104     delayedExpand : function(delay){
17105         if(!this.expandProcId){
17106             this.expandProcId = this.expand.defer(delay, this);
17107         }
17108     },
17109
17110     // private
17111     cancelExpand : function(){
17112         if(this.expandProcId){
17113             clearTimeout(this.expandProcId);
17114         }
17115         this.expandProcId = false;
17116     },
17117
17118     /**
17119      * Toggles expanded/collapsed state of the node
17120      */
17121     toggle : function(){
17122         if(this.expanded){
17123             this.collapse();
17124         }else{
17125             this.expand();
17126         }
17127     },
17128
17129     /**
17130      * Ensures all parent nodes are expanded
17131      */
17132     ensureVisible : function(callback){
17133         var tree = this.getOwnerTree();
17134         tree.expandPath(this.parentNode.getPath(), false, function(){
17135             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17136             Roo.callback(callback);
17137         }.createDelegate(this));
17138     },
17139
17140     /**
17141      * Expand all child nodes
17142      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17143      */
17144     expandChildNodes : function(deep){
17145         var cs = this.childNodes;
17146         for(var i = 0, len = cs.length; i < len; i++) {
17147                 cs[i].expand(deep);
17148         }
17149     },
17150
17151     /**
17152      * Collapse all child nodes
17153      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17154      */
17155     collapseChildNodes : function(deep){
17156         var cs = this.childNodes;
17157         for(var i = 0, len = cs.length; i < len; i++) {
17158                 cs[i].collapse(deep);
17159         }
17160     },
17161
17162     /**
17163      * Disables this node
17164      */
17165     disable : function(){
17166         this.disabled = true;
17167         this.unselect();
17168         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17169             this.ui.onDisableChange(this, true);
17170         }
17171         this.fireEvent("disabledchange", this, true);
17172     },
17173
17174     /**
17175      * Enables this node
17176      */
17177     enable : function(){
17178         this.disabled = false;
17179         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17180             this.ui.onDisableChange(this, false);
17181         }
17182         this.fireEvent("disabledchange", this, false);
17183     },
17184
17185     // private
17186     renderChildren : function(suppressEvent){
17187         if(suppressEvent !== false){
17188             this.fireEvent("beforechildrenrendered", this);
17189         }
17190         var cs = this.childNodes;
17191         for(var i = 0, len = cs.length; i < len; i++){
17192             cs[i].render(true);
17193         }
17194         this.childrenRendered = true;
17195     },
17196
17197     // private
17198     sort : function(fn, scope){
17199         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17200         if(this.childrenRendered){
17201             var cs = this.childNodes;
17202             for(var i = 0, len = cs.length; i < len; i++){
17203                 cs[i].render(true);
17204             }
17205         }
17206     },
17207
17208     // private
17209     render : function(bulkRender){
17210         this.ui.render(bulkRender);
17211         if(!this.rendered){
17212             this.rendered = true;
17213             if(this.expanded){
17214                 this.expanded = false;
17215                 this.expand(false, false);
17216             }
17217         }
17218     },
17219
17220     // private
17221     renderIndent : function(deep, refresh){
17222         if(refresh){
17223             this.ui.childIndent = null;
17224         }
17225         this.ui.renderIndent();
17226         if(deep === true && this.childrenRendered){
17227             var cs = this.childNodes;
17228             for(var i = 0, len = cs.length; i < len; i++){
17229                 cs[i].renderIndent(true, refresh);
17230             }
17231         }
17232     }
17233 });/*
17234  * Based on:
17235  * Ext JS Library 1.1.1
17236  * Copyright(c) 2006-2007, Ext JS, LLC.
17237  *
17238  * Originally Released Under LGPL - original licence link has changed is not relivant.
17239  *
17240  * Fork - LGPL
17241  * <script type="text/javascript">
17242  */
17243  
17244 /**
17245  * @class Roo.tree.AsyncTreeNode
17246  * @extends Roo.tree.TreeNode
17247  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17248  * @constructor
17249  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17250  */
17251  Roo.tree.AsyncTreeNode = function(config){
17252     this.loaded = false;
17253     this.loading = false;
17254     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17255     /**
17256     * @event beforeload
17257     * Fires before this node is loaded, return false to cancel
17258     * @param {Node} this This node
17259     */
17260     this.addEvents({'beforeload':true, 'load': true});
17261     /**
17262     * @event load
17263     * Fires when this node is loaded
17264     * @param {Node} this This node
17265     */
17266     /**
17267      * The loader used by this node (defaults to using the tree's defined loader)
17268      * @type TreeLoader
17269      * @property loader
17270      */
17271 };
17272 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17273     expand : function(deep, anim, callback){
17274         if(this.loading){ // if an async load is already running, waiting til it's done
17275             var timer;
17276             var f = function(){
17277                 if(!this.loading){ // done loading
17278                     clearInterval(timer);
17279                     this.expand(deep, anim, callback);
17280                 }
17281             }.createDelegate(this);
17282             timer = setInterval(f, 200);
17283             return;
17284         }
17285         if(!this.loaded){
17286             if(this.fireEvent("beforeload", this) === false){
17287                 return;
17288             }
17289             this.loading = true;
17290             this.ui.beforeLoad(this);
17291             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17292             if(loader){
17293                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17294                 return;
17295             }
17296         }
17297         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17298     },
17299     
17300     /**
17301      * Returns true if this node is currently loading
17302      * @return {Boolean}
17303      */
17304     isLoading : function(){
17305         return this.loading;  
17306     },
17307     
17308     loadComplete : function(deep, anim, callback){
17309         this.loading = false;
17310         this.loaded = true;
17311         this.ui.afterLoad(this);
17312         this.fireEvent("load", this);
17313         this.expand(deep, anim, callback);
17314     },
17315     
17316     /**
17317      * Returns true if this node has been loaded
17318      * @return {Boolean}
17319      */
17320     isLoaded : function(){
17321         return this.loaded;
17322     },
17323     
17324     hasChildNodes : function(){
17325         if(!this.isLeaf() && !this.loaded){
17326             return true;
17327         }else{
17328             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17329         }
17330     },
17331
17332     /**
17333      * Trigger a reload for this node
17334      * @param {Function} callback
17335      */
17336     reload : function(callback){
17337         this.collapse(false, false);
17338         while(this.firstChild){
17339             this.removeChild(this.firstChild);
17340         }
17341         this.childrenRendered = false;
17342         this.loaded = false;
17343         if(this.isHiddenRoot()){
17344             this.expanded = false;
17345         }
17346         this.expand(false, false, callback);
17347     }
17348 });/*
17349  * Based on:
17350  * Ext JS Library 1.1.1
17351  * Copyright(c) 2006-2007, Ext JS, LLC.
17352  *
17353  * Originally Released Under LGPL - original licence link has changed is not relivant.
17354  *
17355  * Fork - LGPL
17356  * <script type="text/javascript">
17357  */
17358  
17359 /**
17360  * @class Roo.tree.TreeNodeUI
17361  * @constructor
17362  * @param {Object} node The node to render
17363  * The TreeNode UI implementation is separate from the
17364  * tree implementation. Unless you are customizing the tree UI,
17365  * you should never have to use this directly.
17366  */
17367 Roo.tree.TreeNodeUI = function(node){
17368     this.node = node;
17369     this.rendered = false;
17370     this.animating = false;
17371     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17372 };
17373
17374 Roo.tree.TreeNodeUI.prototype = {
17375     removeChild : function(node){
17376         if(this.rendered){
17377             this.ctNode.removeChild(node.ui.getEl());
17378         }
17379     },
17380
17381     beforeLoad : function(){
17382          this.addClass("x-tree-node-loading");
17383     },
17384
17385     afterLoad : function(){
17386          this.removeClass("x-tree-node-loading");
17387     },
17388
17389     onTextChange : function(node, text, oldText){
17390         if(this.rendered){
17391             this.textNode.innerHTML = text;
17392         }
17393     },
17394
17395     onDisableChange : function(node, state){
17396         this.disabled = state;
17397         if(state){
17398             this.addClass("x-tree-node-disabled");
17399         }else{
17400             this.removeClass("x-tree-node-disabled");
17401         }
17402     },
17403
17404     onSelectedChange : function(state){
17405         if(state){
17406             this.focus();
17407             this.addClass("x-tree-selected");
17408         }else{
17409             //this.blur();
17410             this.removeClass("x-tree-selected");
17411         }
17412     },
17413
17414     onMove : function(tree, node, oldParent, newParent, index, refNode){
17415         this.childIndent = null;
17416         if(this.rendered){
17417             var targetNode = newParent.ui.getContainer();
17418             if(!targetNode){//target not rendered
17419                 this.holder = document.createElement("div");
17420                 this.holder.appendChild(this.wrap);
17421                 return;
17422             }
17423             var insertBefore = refNode ? refNode.ui.getEl() : null;
17424             if(insertBefore){
17425                 targetNode.insertBefore(this.wrap, insertBefore);
17426             }else{
17427                 targetNode.appendChild(this.wrap);
17428             }
17429             this.node.renderIndent(true);
17430         }
17431     },
17432
17433     addClass : function(cls){
17434         if(this.elNode){
17435             Roo.fly(this.elNode).addClass(cls);
17436         }
17437     },
17438
17439     removeClass : function(cls){
17440         if(this.elNode){
17441             Roo.fly(this.elNode).removeClass(cls);
17442         }
17443     },
17444
17445     remove : function(){
17446         if(this.rendered){
17447             this.holder = document.createElement("div");
17448             this.holder.appendChild(this.wrap);
17449         }
17450     },
17451
17452     fireEvent : function(){
17453         return this.node.fireEvent.apply(this.node, arguments);
17454     },
17455
17456     initEvents : function(){
17457         this.node.on("move", this.onMove, this);
17458         var E = Roo.EventManager;
17459         var a = this.anchor;
17460
17461         var el = Roo.fly(a, '_treeui');
17462
17463         if(Roo.isOpera){ // opera render bug ignores the CSS
17464             el.setStyle("text-decoration", "none");
17465         }
17466
17467         el.on("click", this.onClick, this);
17468         el.on("dblclick", this.onDblClick, this);
17469
17470         if(this.checkbox){
17471             Roo.EventManager.on(this.checkbox,
17472                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17473         }
17474
17475         el.on("contextmenu", this.onContextMenu, this);
17476
17477         var icon = Roo.fly(this.iconNode);
17478         icon.on("click", this.onClick, this);
17479         icon.on("dblclick", this.onDblClick, this);
17480         icon.on("contextmenu", this.onContextMenu, this);
17481         E.on(this.ecNode, "click", this.ecClick, this, true);
17482
17483         if(this.node.disabled){
17484             this.addClass("x-tree-node-disabled");
17485         }
17486         if(this.node.hidden){
17487             this.addClass("x-tree-node-disabled");
17488         }
17489         var ot = this.node.getOwnerTree();
17490         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17491         if(dd && (!this.node.isRoot || ot.rootVisible)){
17492             Roo.dd.Registry.register(this.elNode, {
17493                 node: this.node,
17494                 handles: this.getDDHandles(),
17495                 isHandle: false
17496             });
17497         }
17498     },
17499
17500     getDDHandles : function(){
17501         return [this.iconNode, this.textNode];
17502     },
17503
17504     hide : function(){
17505         if(this.rendered){
17506             this.wrap.style.display = "none";
17507         }
17508     },
17509
17510     show : function(){
17511         if(this.rendered){
17512             this.wrap.style.display = "";
17513         }
17514     },
17515
17516     onContextMenu : function(e){
17517         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17518             e.preventDefault();
17519             this.focus();
17520             this.fireEvent("contextmenu", this.node, e);
17521         }
17522     },
17523
17524     onClick : function(e){
17525         if(this.dropping){
17526             e.stopEvent();
17527             return;
17528         }
17529         if(this.fireEvent("beforeclick", this.node, e) !== false){
17530             if(!this.disabled && this.node.attributes.href){
17531                 this.fireEvent("click", this.node, e);
17532                 return;
17533             }
17534             e.preventDefault();
17535             if(this.disabled){
17536                 return;
17537             }
17538
17539             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17540                 this.node.toggle();
17541             }
17542
17543             this.fireEvent("click", this.node, e);
17544         }else{
17545             e.stopEvent();
17546         }
17547     },
17548
17549     onDblClick : function(e){
17550         e.preventDefault();
17551         if(this.disabled){
17552             return;
17553         }
17554         if(this.checkbox){
17555             this.toggleCheck();
17556         }
17557         if(!this.animating && this.node.hasChildNodes()){
17558             this.node.toggle();
17559         }
17560         this.fireEvent("dblclick", this.node, e);
17561     },
17562
17563     onCheckChange : function(){
17564         var checked = this.checkbox.checked;
17565         this.node.attributes.checked = checked;
17566         this.fireEvent('checkchange', this.node, checked);
17567     },
17568
17569     ecClick : function(e){
17570         if(!this.animating && this.node.hasChildNodes()){
17571             this.node.toggle();
17572         }
17573     },
17574
17575     startDrop : function(){
17576         this.dropping = true;
17577     },
17578
17579     // delayed drop so the click event doesn't get fired on a drop
17580     endDrop : function(){
17581        setTimeout(function(){
17582            this.dropping = false;
17583        }.createDelegate(this), 50);
17584     },
17585
17586     expand : function(){
17587         this.updateExpandIcon();
17588         this.ctNode.style.display = "";
17589     },
17590
17591     focus : function(){
17592         if(!this.node.preventHScroll){
17593             try{this.anchor.focus();
17594             }catch(e){}
17595         }else if(!Roo.isIE){
17596             try{
17597                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17598                 var l = noscroll.scrollLeft;
17599                 this.anchor.focus();
17600                 noscroll.scrollLeft = l;
17601             }catch(e){}
17602         }
17603     },
17604
17605     toggleCheck : function(value){
17606         var cb = this.checkbox;
17607         if(cb){
17608             cb.checked = (value === undefined ? !cb.checked : value);
17609         }
17610     },
17611
17612     blur : function(){
17613         try{
17614             this.anchor.blur();
17615         }catch(e){}
17616     },
17617
17618     animExpand : function(callback){
17619         var ct = Roo.get(this.ctNode);
17620         ct.stopFx();
17621         if(!this.node.hasChildNodes()){
17622             this.updateExpandIcon();
17623             this.ctNode.style.display = "";
17624             Roo.callback(callback);
17625             return;
17626         }
17627         this.animating = true;
17628         this.updateExpandIcon();
17629
17630         ct.slideIn('t', {
17631            callback : function(){
17632                this.animating = false;
17633                Roo.callback(callback);
17634             },
17635             scope: this,
17636             duration: this.node.ownerTree.duration || .25
17637         });
17638     },
17639
17640     highlight : function(){
17641         var tree = this.node.getOwnerTree();
17642         Roo.fly(this.wrap).highlight(
17643             tree.hlColor || "C3DAF9",
17644             {endColor: tree.hlBaseColor}
17645         );
17646     },
17647
17648     collapse : function(){
17649         this.updateExpandIcon();
17650         this.ctNode.style.display = "none";
17651     },
17652
17653     animCollapse : function(callback){
17654         var ct = Roo.get(this.ctNode);
17655         ct.enableDisplayMode('block');
17656         ct.stopFx();
17657
17658         this.animating = true;
17659         this.updateExpandIcon();
17660
17661         ct.slideOut('t', {
17662             callback : function(){
17663                this.animating = false;
17664                Roo.callback(callback);
17665             },
17666             scope: this,
17667             duration: this.node.ownerTree.duration || .25
17668         });
17669     },
17670
17671     getContainer : function(){
17672         return this.ctNode;
17673     },
17674
17675     getEl : function(){
17676         return this.wrap;
17677     },
17678
17679     appendDDGhost : function(ghostNode){
17680         ghostNode.appendChild(this.elNode.cloneNode(true));
17681     },
17682
17683     getDDRepairXY : function(){
17684         return Roo.lib.Dom.getXY(this.iconNode);
17685     },
17686
17687     onRender : function(){
17688         this.render();
17689     },
17690
17691     render : function(bulkRender){
17692         var n = this.node, a = n.attributes;
17693         var targetNode = n.parentNode ?
17694               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17695
17696         if(!this.rendered){
17697             this.rendered = true;
17698
17699             this.renderElements(n, a, targetNode, bulkRender);
17700
17701             if(a.qtip){
17702                if(this.textNode.setAttributeNS){
17703                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17704                    if(a.qtipTitle){
17705                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17706                    }
17707                }else{
17708                    this.textNode.setAttribute("ext:qtip", a.qtip);
17709                    if(a.qtipTitle){
17710                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17711                    }
17712                }
17713             }else if(a.qtipCfg){
17714                 a.qtipCfg.target = Roo.id(this.textNode);
17715                 Roo.QuickTips.register(a.qtipCfg);
17716             }
17717             this.initEvents();
17718             if(!this.node.expanded){
17719                 this.updateExpandIcon();
17720             }
17721         }else{
17722             if(bulkRender === true) {
17723                 targetNode.appendChild(this.wrap);
17724             }
17725         }
17726     },
17727
17728     renderElements : function(n, a, targetNode, bulkRender)
17729     {
17730         // add some indent caching, this helps performance when rendering a large tree
17731         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17732         var t = n.getOwnerTree();
17733         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17734         if (typeof(n.attributes.html) != 'undefined') {
17735             txt = n.attributes.html;
17736         }
17737         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17738         var cb = typeof a.checked == 'boolean';
17739         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17740         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17741             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17742             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17743             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17744             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17745             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17746              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17747                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17748             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17749             "</li>"];
17750
17751         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17752             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17753                                 n.nextSibling.ui.getEl(), buf.join(""));
17754         }else{
17755             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17756         }
17757
17758         this.elNode = this.wrap.childNodes[0];
17759         this.ctNode = this.wrap.childNodes[1];
17760         var cs = this.elNode.childNodes;
17761         this.indentNode = cs[0];
17762         this.ecNode = cs[1];
17763         this.iconNode = cs[2];
17764         var index = 3;
17765         if(cb){
17766             this.checkbox = cs[3];
17767             index++;
17768         }
17769         this.anchor = cs[index];
17770         this.textNode = cs[index].firstChild;
17771     },
17772
17773     getAnchor : function(){
17774         return this.anchor;
17775     },
17776
17777     getTextEl : function(){
17778         return this.textNode;
17779     },
17780
17781     getIconEl : function(){
17782         return this.iconNode;
17783     },
17784
17785     isChecked : function(){
17786         return this.checkbox ? this.checkbox.checked : false;
17787     },
17788
17789     updateExpandIcon : function(){
17790         if(this.rendered){
17791             var n = this.node, c1, c2;
17792             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17793             var hasChild = n.hasChildNodes();
17794             if(hasChild){
17795                 if(n.expanded){
17796                     cls += "-minus";
17797                     c1 = "x-tree-node-collapsed";
17798                     c2 = "x-tree-node-expanded";
17799                 }else{
17800                     cls += "-plus";
17801                     c1 = "x-tree-node-expanded";
17802                     c2 = "x-tree-node-collapsed";
17803                 }
17804                 if(this.wasLeaf){
17805                     this.removeClass("x-tree-node-leaf");
17806                     this.wasLeaf = false;
17807                 }
17808                 if(this.c1 != c1 || this.c2 != c2){
17809                     Roo.fly(this.elNode).replaceClass(c1, c2);
17810                     this.c1 = c1; this.c2 = c2;
17811                 }
17812             }else{
17813                 // this changes non-leafs into leafs if they have no children.
17814                 // it's not very rational behaviour..
17815                 
17816                 if(!this.wasLeaf && this.node.leaf){
17817                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17818                     delete this.c1;
17819                     delete this.c2;
17820                     this.wasLeaf = true;
17821                 }
17822             }
17823             var ecc = "x-tree-ec-icon "+cls;
17824             if(this.ecc != ecc){
17825                 this.ecNode.className = ecc;
17826                 this.ecc = ecc;
17827             }
17828         }
17829     },
17830
17831     getChildIndent : function(){
17832         if(!this.childIndent){
17833             var buf = [];
17834             var p = this.node;
17835             while(p){
17836                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17837                     if(!p.isLast()) {
17838                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17839                     } else {
17840                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17841                     }
17842                 }
17843                 p = p.parentNode;
17844             }
17845             this.childIndent = buf.join("");
17846         }
17847         return this.childIndent;
17848     },
17849
17850     renderIndent : function(){
17851         if(this.rendered){
17852             var indent = "";
17853             var p = this.node.parentNode;
17854             if(p){
17855                 indent = p.ui.getChildIndent();
17856             }
17857             if(this.indentMarkup != indent){ // don't rerender if not required
17858                 this.indentNode.innerHTML = indent;
17859                 this.indentMarkup = indent;
17860             }
17861             this.updateExpandIcon();
17862         }
17863     }
17864 };
17865
17866 Roo.tree.RootTreeNodeUI = function(){
17867     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17868 };
17869 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17870     render : function(){
17871         if(!this.rendered){
17872             var targetNode = this.node.ownerTree.innerCt.dom;
17873             this.node.expanded = true;
17874             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17875             this.wrap = this.ctNode = targetNode.firstChild;
17876         }
17877     },
17878     collapse : function(){
17879     },
17880     expand : function(){
17881     }
17882 });/*
17883  * Based on:
17884  * Ext JS Library 1.1.1
17885  * Copyright(c) 2006-2007, Ext JS, LLC.
17886  *
17887  * Originally Released Under LGPL - original licence link has changed is not relivant.
17888  *
17889  * Fork - LGPL
17890  * <script type="text/javascript">
17891  */
17892 /**
17893  * @class Roo.tree.TreeLoader
17894  * @extends Roo.util.Observable
17895  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17896  * nodes from a specified URL. The response must be a javascript Array definition
17897  * who's elements are node definition objects. eg:
17898  * <pre><code>
17899 {  success : true,
17900    data :      [
17901    
17902     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17903     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17904     ]
17905 }
17906
17907
17908 </code></pre>
17909  * <br><br>
17910  * The old style respose with just an array is still supported, but not recommended.
17911  * <br><br>
17912  *
17913  * A server request is sent, and child nodes are loaded only when a node is expanded.
17914  * The loading node's id is passed to the server under the parameter name "node" to
17915  * enable the server to produce the correct child nodes.
17916  * <br><br>
17917  * To pass extra parameters, an event handler may be attached to the "beforeload"
17918  * event, and the parameters specified in the TreeLoader's baseParams property:
17919  * <pre><code>
17920     myTreeLoader.on("beforeload", function(treeLoader, node) {
17921         this.baseParams.category = node.attributes.category;
17922     }, this);
17923 </code></pre><
17924  * This would pass an HTTP parameter called "category" to the server containing
17925  * the value of the Node's "category" attribute.
17926  * @constructor
17927  * Creates a new Treeloader.
17928  * @param {Object} config A config object containing config properties.
17929  */
17930 Roo.tree.TreeLoader = function(config){
17931     this.baseParams = {};
17932     this.requestMethod = "POST";
17933     Roo.apply(this, config);
17934
17935     this.addEvents({
17936     
17937         /**
17938          * @event beforeload
17939          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17940          * @param {Object} This TreeLoader object.
17941          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17942          * @param {Object} callback The callback function specified in the {@link #load} call.
17943          */
17944         beforeload : true,
17945         /**
17946          * @event load
17947          * Fires when the node has been successfuly loaded.
17948          * @param {Object} This TreeLoader object.
17949          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17950          * @param {Object} response The response object containing the data from the server.
17951          */
17952         load : true,
17953         /**
17954          * @event loadexception
17955          * Fires if the network request failed.
17956          * @param {Object} This TreeLoader object.
17957          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17958          * @param {Object} response The response object containing the data from the server.
17959          */
17960         loadexception : true,
17961         /**
17962          * @event create
17963          * Fires before a node is created, enabling you to return custom Node types 
17964          * @param {Object} This TreeLoader object.
17965          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17966          */
17967         create : true
17968     });
17969
17970     Roo.tree.TreeLoader.superclass.constructor.call(this);
17971 };
17972
17973 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17974     /**
17975     * @cfg {String} dataUrl The URL from which to request a Json string which
17976     * specifies an array of node definition object representing the child nodes
17977     * to be loaded.
17978     */
17979     /**
17980     * @cfg {String} requestMethod either GET or POST
17981     * defaults to POST (due to BC)
17982     * to be loaded.
17983     */
17984     /**
17985     * @cfg {Object} baseParams (optional) An object containing properties which
17986     * specify HTTP parameters to be passed to each request for child nodes.
17987     */
17988     /**
17989     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17990     * created by this loader. If the attributes sent by the server have an attribute in this object,
17991     * they take priority.
17992     */
17993     /**
17994     * @cfg {Object} uiProviders (optional) An object containing properties which
17995     * 
17996     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17997     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17998     * <i>uiProvider</i> attribute of a returned child node is a string rather
17999     * than a reference to a TreeNodeUI implementation, this that string value
18000     * is used as a property name in the uiProviders object. You can define the provider named
18001     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18002     */
18003     uiProviders : {},
18004
18005     /**
18006     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18007     * child nodes before loading.
18008     */
18009     clearOnLoad : true,
18010
18011     /**
18012     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18013     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18014     * Grid query { data : [ .....] }
18015     */
18016     
18017     root : false,
18018      /**
18019     * @cfg {String} queryParam (optional) 
18020     * Name of the query as it will be passed on the querystring (defaults to 'node')
18021     * eg. the request will be ?node=[id]
18022     */
18023     
18024     
18025     queryParam: false,
18026     
18027     /**
18028      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18029      * This is called automatically when a node is expanded, but may be used to reload
18030      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18031      * @param {Roo.tree.TreeNode} node
18032      * @param {Function} callback
18033      */
18034     load : function(node, callback){
18035         if(this.clearOnLoad){
18036             while(node.firstChild){
18037                 node.removeChild(node.firstChild);
18038             }
18039         }
18040         if(node.attributes.children){ // preloaded json children
18041             var cs = node.attributes.children;
18042             for(var i = 0, len = cs.length; i < len; i++){
18043                 node.appendChild(this.createNode(cs[i]));
18044             }
18045             if(typeof callback == "function"){
18046                 callback();
18047             }
18048         }else if(this.dataUrl){
18049             this.requestData(node, callback);
18050         }
18051     },
18052
18053     getParams: function(node){
18054         var buf = [], bp = this.baseParams;
18055         for(var key in bp){
18056             if(typeof bp[key] != "function"){
18057                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18058             }
18059         }
18060         var n = this.queryParam === false ? 'node' : this.queryParam;
18061         buf.push(n + "=", encodeURIComponent(node.id));
18062         return buf.join("");
18063     },
18064
18065     requestData : function(node, callback){
18066         if(this.fireEvent("beforeload", this, node, callback) !== false){
18067             this.transId = Roo.Ajax.request({
18068                 method:this.requestMethod,
18069                 url: this.dataUrl||this.url,
18070                 success: this.handleResponse,
18071                 failure: this.handleFailure,
18072                 scope: this,
18073                 argument: {callback: callback, node: node},
18074                 params: this.getParams(node)
18075             });
18076         }else{
18077             // if the load is cancelled, make sure we notify
18078             // the node that we are done
18079             if(typeof callback == "function"){
18080                 callback();
18081             }
18082         }
18083     },
18084
18085     isLoading : function(){
18086         return this.transId ? true : false;
18087     },
18088
18089     abort : function(){
18090         if(this.isLoading()){
18091             Roo.Ajax.abort(this.transId);
18092         }
18093     },
18094
18095     // private
18096     createNode : function(attr)
18097     {
18098         // apply baseAttrs, nice idea Corey!
18099         if(this.baseAttrs){
18100             Roo.applyIf(attr, this.baseAttrs);
18101         }
18102         if(this.applyLoader !== false){
18103             attr.loader = this;
18104         }
18105         // uiProvider = depreciated..
18106         
18107         if(typeof(attr.uiProvider) == 'string'){
18108            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18109                 /**  eval:var:attr */ eval(attr.uiProvider);
18110         }
18111         if(typeof(this.uiProviders['default']) != 'undefined') {
18112             attr.uiProvider = this.uiProviders['default'];
18113         }
18114         
18115         this.fireEvent('create', this, attr);
18116         
18117         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18118         return(attr.leaf ?
18119                         new Roo.tree.TreeNode(attr) :
18120                         new Roo.tree.AsyncTreeNode(attr));
18121     },
18122
18123     processResponse : function(response, node, callback)
18124     {
18125         var json = response.responseText;
18126         try {
18127             
18128             var o = Roo.decode(json);
18129             
18130             if (this.root === false && typeof(o.success) != undefined) {
18131                 this.root = 'data'; // the default behaviour for list like data..
18132                 }
18133                 
18134             if (this.root !== false &&  !o.success) {
18135                 // it's a failure condition.
18136                 var a = response.argument;
18137                 this.fireEvent("loadexception", this, a.node, response);
18138                 Roo.log("Load failed - should have a handler really");
18139                 return;
18140             }
18141             
18142             
18143             
18144             if (this.root !== false) {
18145                  o = o[this.root];
18146             }
18147             
18148             for(var i = 0, len = o.length; i < len; i++){
18149                 var n = this.createNode(o[i]);
18150                 if(n){
18151                     node.appendChild(n);
18152                 }
18153             }
18154             if(typeof callback == "function"){
18155                 callback(this, node);
18156             }
18157         }catch(e){
18158             this.handleFailure(response);
18159         }
18160     },
18161
18162     handleResponse : function(response){
18163         this.transId = false;
18164         var a = response.argument;
18165         this.processResponse(response, a.node, a.callback);
18166         this.fireEvent("load", this, a.node, response);
18167     },
18168
18169     handleFailure : function(response)
18170     {
18171         // should handle failure better..
18172         this.transId = false;
18173         var a = response.argument;
18174         this.fireEvent("loadexception", this, a.node, response);
18175         if(typeof a.callback == "function"){
18176             a.callback(this, a.node);
18177         }
18178     }
18179 });/*
18180  * Based on:
18181  * Ext JS Library 1.1.1
18182  * Copyright(c) 2006-2007, Ext JS, LLC.
18183  *
18184  * Originally Released Under LGPL - original licence link has changed is not relivant.
18185  *
18186  * Fork - LGPL
18187  * <script type="text/javascript">
18188  */
18189
18190 /**
18191 * @class Roo.tree.TreeFilter
18192 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18193 * @param {TreePanel} tree
18194 * @param {Object} config (optional)
18195  */
18196 Roo.tree.TreeFilter = function(tree, config){
18197     this.tree = tree;
18198     this.filtered = {};
18199     Roo.apply(this, config);
18200 };
18201
18202 Roo.tree.TreeFilter.prototype = {
18203     clearBlank:false,
18204     reverse:false,
18205     autoClear:false,
18206     remove:false,
18207
18208      /**
18209      * Filter the data by a specific attribute.
18210      * @param {String/RegExp} value Either string that the attribute value
18211      * should start with or a RegExp to test against the attribute
18212      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18213      * @param {TreeNode} startNode (optional) The node to start the filter at.
18214      */
18215     filter : function(value, attr, startNode){
18216         attr = attr || "text";
18217         var f;
18218         if(typeof value == "string"){
18219             var vlen = value.length;
18220             // auto clear empty filter
18221             if(vlen == 0 && this.clearBlank){
18222                 this.clear();
18223                 return;
18224             }
18225             value = value.toLowerCase();
18226             f = function(n){
18227                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18228             };
18229         }else if(value.exec){ // regex?
18230             f = function(n){
18231                 return value.test(n.attributes[attr]);
18232             };
18233         }else{
18234             throw 'Illegal filter type, must be string or regex';
18235         }
18236         this.filterBy(f, null, startNode);
18237         },
18238
18239     /**
18240      * Filter by a function. The passed function will be called with each
18241      * node in the tree (or from the startNode). If the function returns true, the node is kept
18242      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18243      * @param {Function} fn The filter function
18244      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18245      */
18246     filterBy : function(fn, scope, startNode){
18247         startNode = startNode || this.tree.root;
18248         if(this.autoClear){
18249             this.clear();
18250         }
18251         var af = this.filtered, rv = this.reverse;
18252         var f = function(n){
18253             if(n == startNode){
18254                 return true;
18255             }
18256             if(af[n.id]){
18257                 return false;
18258             }
18259             var m = fn.call(scope || n, n);
18260             if(!m || rv){
18261                 af[n.id] = n;
18262                 n.ui.hide();
18263                 return false;
18264             }
18265             return true;
18266         };
18267         startNode.cascade(f);
18268         if(this.remove){
18269            for(var id in af){
18270                if(typeof id != "function"){
18271                    var n = af[id];
18272                    if(n && n.parentNode){
18273                        n.parentNode.removeChild(n);
18274                    }
18275                }
18276            }
18277         }
18278     },
18279
18280     /**
18281      * Clears the current filter. Note: with the "remove" option
18282      * set a filter cannot be cleared.
18283      */
18284     clear : function(){
18285         var t = this.tree;
18286         var af = this.filtered;
18287         for(var id in af){
18288             if(typeof id != "function"){
18289                 var n = af[id];
18290                 if(n){
18291                     n.ui.show();
18292                 }
18293             }
18294         }
18295         this.filtered = {};
18296     }
18297 };
18298 /*
18299  * Based on:
18300  * Ext JS Library 1.1.1
18301  * Copyright(c) 2006-2007, Ext JS, LLC.
18302  *
18303  * Originally Released Under LGPL - original licence link has changed is not relivant.
18304  *
18305  * Fork - LGPL
18306  * <script type="text/javascript">
18307  */
18308  
18309
18310 /**
18311  * @class Roo.tree.TreeSorter
18312  * Provides sorting of nodes in a TreePanel
18313  * 
18314  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18315  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18316  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18317  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18318  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18319  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18320  * @constructor
18321  * @param {TreePanel} tree
18322  * @param {Object} config
18323  */
18324 Roo.tree.TreeSorter = function(tree, config){
18325     Roo.apply(this, config);
18326     tree.on("beforechildrenrendered", this.doSort, this);
18327     tree.on("append", this.updateSort, this);
18328     tree.on("insert", this.updateSort, this);
18329     
18330     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18331     var p = this.property || "text";
18332     var sortType = this.sortType;
18333     var fs = this.folderSort;
18334     var cs = this.caseSensitive === true;
18335     var leafAttr = this.leafAttr || 'leaf';
18336
18337     this.sortFn = function(n1, n2){
18338         if(fs){
18339             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18340                 return 1;
18341             }
18342             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18343                 return -1;
18344             }
18345         }
18346         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18347         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18348         if(v1 < v2){
18349                         return dsc ? +1 : -1;
18350                 }else if(v1 > v2){
18351                         return dsc ? -1 : +1;
18352         }else{
18353                 return 0;
18354         }
18355     };
18356 };
18357
18358 Roo.tree.TreeSorter.prototype = {
18359     doSort : function(node){
18360         node.sort(this.sortFn);
18361     },
18362     
18363     compareNodes : function(n1, n2){
18364         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18365     },
18366     
18367     updateSort : function(tree, node){
18368         if(node.childrenRendered){
18369             this.doSort.defer(1, this, [node]);
18370         }
18371     }
18372 };/*
18373  * Based on:
18374  * Ext JS Library 1.1.1
18375  * Copyright(c) 2006-2007, Ext JS, LLC.
18376  *
18377  * Originally Released Under LGPL - original licence link has changed is not relivant.
18378  *
18379  * Fork - LGPL
18380  * <script type="text/javascript">
18381  */
18382
18383 if(Roo.dd.DropZone){
18384     
18385 Roo.tree.TreeDropZone = function(tree, config){
18386     this.allowParentInsert = false;
18387     this.allowContainerDrop = false;
18388     this.appendOnly = false;
18389     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18390     this.tree = tree;
18391     this.lastInsertClass = "x-tree-no-status";
18392     this.dragOverData = {};
18393 };
18394
18395 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18396     ddGroup : "TreeDD",
18397     scroll:  true,
18398     
18399     expandDelay : 1000,
18400     
18401     expandNode : function(node){
18402         if(node.hasChildNodes() && !node.isExpanded()){
18403             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18404         }
18405     },
18406     
18407     queueExpand : function(node){
18408         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18409     },
18410     
18411     cancelExpand : function(){
18412         if(this.expandProcId){
18413             clearTimeout(this.expandProcId);
18414             this.expandProcId = false;
18415         }
18416     },
18417     
18418     isValidDropPoint : function(n, pt, dd, e, data){
18419         if(!n || !data){ return false; }
18420         var targetNode = n.node;
18421         var dropNode = data.node;
18422         // default drop rules
18423         if(!(targetNode && targetNode.isTarget && pt)){
18424             return false;
18425         }
18426         if(pt == "append" && targetNode.allowChildren === false){
18427             return false;
18428         }
18429         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18430             return false;
18431         }
18432         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18433             return false;
18434         }
18435         // reuse the object
18436         var overEvent = this.dragOverData;
18437         overEvent.tree = this.tree;
18438         overEvent.target = targetNode;
18439         overEvent.data = data;
18440         overEvent.point = pt;
18441         overEvent.source = dd;
18442         overEvent.rawEvent = e;
18443         overEvent.dropNode = dropNode;
18444         overEvent.cancel = false;  
18445         var result = this.tree.fireEvent("nodedragover", overEvent);
18446         return overEvent.cancel === false && result !== false;
18447     },
18448     
18449     getDropPoint : function(e, n, dd)
18450     {
18451         var tn = n.node;
18452         if(tn.isRoot){
18453             return tn.allowChildren !== false ? "append" : false; // always append for root
18454         }
18455         var dragEl = n.ddel;
18456         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18457         var y = Roo.lib.Event.getPageY(e);
18458         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18459         
18460         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18461         var noAppend = tn.allowChildren === false;
18462         if(this.appendOnly || tn.parentNode.allowChildren === false){
18463             return noAppend ? false : "append";
18464         }
18465         var noBelow = false;
18466         if(!this.allowParentInsert){
18467             noBelow = tn.hasChildNodes() && tn.isExpanded();
18468         }
18469         var q = (b - t) / (noAppend ? 2 : 3);
18470         if(y >= t && y < (t + q)){
18471             return "above";
18472         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18473             return "below";
18474         }else{
18475             return "append";
18476         }
18477     },
18478     
18479     onNodeEnter : function(n, dd, e, data)
18480     {
18481         this.cancelExpand();
18482     },
18483     
18484     onNodeOver : function(n, dd, e, data)
18485     {
18486        
18487         var pt = this.getDropPoint(e, n, dd);
18488         var node = n.node;
18489         
18490         // auto node expand check
18491         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18492             this.queueExpand(node);
18493         }else if(pt != "append"){
18494             this.cancelExpand();
18495         }
18496         
18497         // set the insert point style on the target node
18498         var returnCls = this.dropNotAllowed;
18499         if(this.isValidDropPoint(n, pt, dd, e, data)){
18500            if(pt){
18501                var el = n.ddel;
18502                var cls;
18503                if(pt == "above"){
18504                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18505                    cls = "x-tree-drag-insert-above";
18506                }else if(pt == "below"){
18507                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18508                    cls = "x-tree-drag-insert-below";
18509                }else{
18510                    returnCls = "x-tree-drop-ok-append";
18511                    cls = "x-tree-drag-append";
18512                }
18513                if(this.lastInsertClass != cls){
18514                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18515                    this.lastInsertClass = cls;
18516                }
18517            }
18518        }
18519        return returnCls;
18520     },
18521     
18522     onNodeOut : function(n, dd, e, data){
18523         
18524         this.cancelExpand();
18525         this.removeDropIndicators(n);
18526     },
18527     
18528     onNodeDrop : function(n, dd, e, data){
18529         var point = this.getDropPoint(e, n, dd);
18530         var targetNode = n.node;
18531         targetNode.ui.startDrop();
18532         if(!this.isValidDropPoint(n, point, dd, e, data)){
18533             targetNode.ui.endDrop();
18534             return false;
18535         }
18536         // first try to find the drop node
18537         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18538         var dropEvent = {
18539             tree : this.tree,
18540             target: targetNode,
18541             data: data,
18542             point: point,
18543             source: dd,
18544             rawEvent: e,
18545             dropNode: dropNode,
18546             cancel: !dropNode   
18547         };
18548         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18549         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18550             targetNode.ui.endDrop();
18551             return false;
18552         }
18553         // allow target changing
18554         targetNode = dropEvent.target;
18555         if(point == "append" && !targetNode.isExpanded()){
18556             targetNode.expand(false, null, function(){
18557                 this.completeDrop(dropEvent);
18558             }.createDelegate(this));
18559         }else{
18560             this.completeDrop(dropEvent);
18561         }
18562         return true;
18563     },
18564     
18565     completeDrop : function(de){
18566         var ns = de.dropNode, p = de.point, t = de.target;
18567         if(!(ns instanceof Array)){
18568             ns = [ns];
18569         }
18570         var n;
18571         for(var i = 0, len = ns.length; i < len; i++){
18572             n = ns[i];
18573             if(p == "above"){
18574                 t.parentNode.insertBefore(n, t);
18575             }else if(p == "below"){
18576                 t.parentNode.insertBefore(n, t.nextSibling);
18577             }else{
18578                 t.appendChild(n);
18579             }
18580         }
18581         n.ui.focus();
18582         if(this.tree.hlDrop){
18583             n.ui.highlight();
18584         }
18585         t.ui.endDrop();
18586         this.tree.fireEvent("nodedrop", de);
18587     },
18588     
18589     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18590         if(this.tree.hlDrop){
18591             dropNode.ui.focus();
18592             dropNode.ui.highlight();
18593         }
18594         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18595     },
18596     
18597     getTree : function(){
18598         return this.tree;
18599     },
18600     
18601     removeDropIndicators : function(n){
18602         if(n && n.ddel){
18603             var el = n.ddel;
18604             Roo.fly(el).removeClass([
18605                     "x-tree-drag-insert-above",
18606                     "x-tree-drag-insert-below",
18607                     "x-tree-drag-append"]);
18608             this.lastInsertClass = "_noclass";
18609         }
18610     },
18611     
18612     beforeDragDrop : function(target, e, id){
18613         this.cancelExpand();
18614         return true;
18615     },
18616     
18617     afterRepair : function(data){
18618         if(data && Roo.enableFx){
18619             data.node.ui.highlight();
18620         }
18621         this.hideProxy();
18622     } 
18623     
18624 });
18625
18626 }
18627 /*
18628  * Based on:
18629  * Ext JS Library 1.1.1
18630  * Copyright(c) 2006-2007, Ext JS, LLC.
18631  *
18632  * Originally Released Under LGPL - original licence link has changed is not relivant.
18633  *
18634  * Fork - LGPL
18635  * <script type="text/javascript">
18636  */
18637  
18638
18639 if(Roo.dd.DragZone){
18640 Roo.tree.TreeDragZone = function(tree, config){
18641     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18642     this.tree = tree;
18643 };
18644
18645 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18646     ddGroup : "TreeDD",
18647    
18648     onBeforeDrag : function(data, e){
18649         var n = data.node;
18650         return n && n.draggable && !n.disabled;
18651     },
18652      
18653     
18654     onInitDrag : function(e){
18655         var data = this.dragData;
18656         this.tree.getSelectionModel().select(data.node);
18657         this.proxy.update("");
18658         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18659         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18660     },
18661     
18662     getRepairXY : function(e, data){
18663         return data.node.ui.getDDRepairXY();
18664     },
18665     
18666     onEndDrag : function(data, e){
18667         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18668         
18669         
18670     },
18671     
18672     onValidDrop : function(dd, e, id){
18673         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18674         this.hideProxy();
18675     },
18676     
18677     beforeInvalidDrop : function(e, id){
18678         // this scrolls the original position back into view
18679         var sm = this.tree.getSelectionModel();
18680         sm.clearSelections();
18681         sm.select(this.dragData.node);
18682     }
18683 });
18684 }/*
18685  * Based on:
18686  * Ext JS Library 1.1.1
18687  * Copyright(c) 2006-2007, Ext JS, LLC.
18688  *
18689  * Originally Released Under LGPL - original licence link has changed is not relivant.
18690  *
18691  * Fork - LGPL
18692  * <script type="text/javascript">
18693  */
18694 /**
18695  * @class Roo.tree.TreeEditor
18696  * @extends Roo.Editor
18697  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18698  * as the editor field.
18699  * @constructor
18700  * @param {Object} config (used to be the tree panel.)
18701  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18702  * 
18703  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18704  * @cfg {Roo.form.TextField|Object} field The field configuration
18705  *
18706  * 
18707  */
18708 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18709     var tree = config;
18710     var field;
18711     if (oldconfig) { // old style..
18712         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18713     } else {
18714         // new style..
18715         tree = config.tree;
18716         config.field = config.field  || {};
18717         config.field.xtype = 'TextField';
18718         field = Roo.factory(config.field, Roo.form);
18719     }
18720     config = config || {};
18721     
18722     
18723     this.addEvents({
18724         /**
18725          * @event beforenodeedit
18726          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18727          * false from the handler of this event.
18728          * @param {Editor} this
18729          * @param {Roo.tree.Node} node 
18730          */
18731         "beforenodeedit" : true
18732     });
18733     
18734     //Roo.log(config);
18735     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18736
18737     this.tree = tree;
18738
18739     tree.on('beforeclick', this.beforeNodeClick, this);
18740     tree.getTreeEl().on('mousedown', this.hide, this);
18741     this.on('complete', this.updateNode, this);
18742     this.on('beforestartedit', this.fitToTree, this);
18743     this.on('startedit', this.bindScroll, this, {delay:10});
18744     this.on('specialkey', this.onSpecialKey, this);
18745 };
18746
18747 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18748     /**
18749      * @cfg {String} alignment
18750      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18751      */
18752     alignment: "l-l",
18753     // inherit
18754     autoSize: false,
18755     /**
18756      * @cfg {Boolean} hideEl
18757      * True to hide the bound element while the editor is displayed (defaults to false)
18758      */
18759     hideEl : false,
18760     /**
18761      * @cfg {String} cls
18762      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18763      */
18764     cls: "x-small-editor x-tree-editor",
18765     /**
18766      * @cfg {Boolean} shim
18767      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18768      */
18769     shim:false,
18770     // inherit
18771     shadow:"frame",
18772     /**
18773      * @cfg {Number} maxWidth
18774      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18775      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18776      * scroll and client offsets into account prior to each edit.
18777      */
18778     maxWidth: 250,
18779
18780     editDelay : 350,
18781
18782     // private
18783     fitToTree : function(ed, el){
18784         var td = this.tree.getTreeEl().dom, nd = el.dom;
18785         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18786             td.scrollLeft = nd.offsetLeft;
18787         }
18788         var w = Math.min(
18789                 this.maxWidth,
18790                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18791         this.setSize(w, '');
18792         
18793         return this.fireEvent('beforenodeedit', this, this.editNode);
18794         
18795     },
18796
18797     // private
18798     triggerEdit : function(node){
18799         this.completeEdit();
18800         this.editNode = node;
18801         this.startEdit(node.ui.textNode, node.text);
18802     },
18803
18804     // private
18805     bindScroll : function(){
18806         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18807     },
18808
18809     // private
18810     beforeNodeClick : function(node, e){
18811         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18812         this.lastClick = new Date();
18813         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18814             e.stopEvent();
18815             this.triggerEdit(node);
18816             return false;
18817         }
18818         return true;
18819     },
18820
18821     // private
18822     updateNode : function(ed, value){
18823         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18824         this.editNode.setText(value);
18825     },
18826
18827     // private
18828     onHide : function(){
18829         Roo.tree.TreeEditor.superclass.onHide.call(this);
18830         if(this.editNode){
18831             this.editNode.ui.focus();
18832         }
18833     },
18834
18835     // private
18836     onSpecialKey : function(field, e){
18837         var k = e.getKey();
18838         if(k == e.ESC){
18839             e.stopEvent();
18840             this.cancelEdit();
18841         }else if(k == e.ENTER && !e.hasModifier()){
18842             e.stopEvent();
18843             this.completeEdit();
18844         }
18845     }
18846 });//<Script type="text/javascript">
18847 /*
18848  * Based on:
18849  * Ext JS Library 1.1.1
18850  * Copyright(c) 2006-2007, Ext JS, LLC.
18851  *
18852  * Originally Released Under LGPL - original licence link has changed is not relivant.
18853  *
18854  * Fork - LGPL
18855  * <script type="text/javascript">
18856  */
18857  
18858 /**
18859  * Not documented??? - probably should be...
18860  */
18861
18862 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18863     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18864     
18865     renderElements : function(n, a, targetNode, bulkRender){
18866         //consel.log("renderElements?");
18867         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18868
18869         var t = n.getOwnerTree();
18870         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18871         
18872         var cols = t.columns;
18873         var bw = t.borderWidth;
18874         var c = cols[0];
18875         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18876          var cb = typeof a.checked == "boolean";
18877         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18878         var colcls = 'x-t-' + tid + '-c0';
18879         var buf = [
18880             '<li class="x-tree-node">',
18881             
18882                 
18883                 '<div class="x-tree-node-el ', a.cls,'">',
18884                     // extran...
18885                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18886                 
18887                 
18888                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18889                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18890                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18891                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18892                            (a.iconCls ? ' '+a.iconCls : ''),
18893                            '" unselectable="on" />',
18894                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18895                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18896                              
18897                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18898                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18899                             '<span unselectable="on" qtip="' + tx + '">',
18900                              tx,
18901                              '</span></a>' ,
18902                     '</div>',
18903                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18904                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18905                  ];
18906         for(var i = 1, len = cols.length; i < len; i++){
18907             c = cols[i];
18908             colcls = 'x-t-' + tid + '-c' +i;
18909             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18910             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18911                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18912                       "</div>");
18913          }
18914          
18915          buf.push(
18916             '</a>',
18917             '<div class="x-clear"></div></div>',
18918             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18919             "</li>");
18920         
18921         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18922             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18923                                 n.nextSibling.ui.getEl(), buf.join(""));
18924         }else{
18925             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18926         }
18927         var el = this.wrap.firstChild;
18928         this.elRow = el;
18929         this.elNode = el.firstChild;
18930         this.ranchor = el.childNodes[1];
18931         this.ctNode = this.wrap.childNodes[1];
18932         var cs = el.firstChild.childNodes;
18933         this.indentNode = cs[0];
18934         this.ecNode = cs[1];
18935         this.iconNode = cs[2];
18936         var index = 3;
18937         if(cb){
18938             this.checkbox = cs[3];
18939             index++;
18940         }
18941         this.anchor = cs[index];
18942         
18943         this.textNode = cs[index].firstChild;
18944         
18945         //el.on("click", this.onClick, this);
18946         //el.on("dblclick", this.onDblClick, this);
18947         
18948         
18949        // console.log(this);
18950     },
18951     initEvents : function(){
18952         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18953         
18954             
18955         var a = this.ranchor;
18956
18957         var el = Roo.get(a);
18958
18959         if(Roo.isOpera){ // opera render bug ignores the CSS
18960             el.setStyle("text-decoration", "none");
18961         }
18962
18963         el.on("click", this.onClick, this);
18964         el.on("dblclick", this.onDblClick, this);
18965         el.on("contextmenu", this.onContextMenu, this);
18966         
18967     },
18968     
18969     /*onSelectedChange : function(state){
18970         if(state){
18971             this.focus();
18972             this.addClass("x-tree-selected");
18973         }else{
18974             //this.blur();
18975             this.removeClass("x-tree-selected");
18976         }
18977     },*/
18978     addClass : function(cls){
18979         if(this.elRow){
18980             Roo.fly(this.elRow).addClass(cls);
18981         }
18982         
18983     },
18984     
18985     
18986     removeClass : function(cls){
18987         if(this.elRow){
18988             Roo.fly(this.elRow).removeClass(cls);
18989         }
18990     }
18991
18992     
18993     
18994 });//<Script type="text/javascript">
18995
18996 /*
18997  * Based on:
18998  * Ext JS Library 1.1.1
18999  * Copyright(c) 2006-2007, Ext JS, LLC.
19000  *
19001  * Originally Released Under LGPL - original licence link has changed is not relivant.
19002  *
19003  * Fork - LGPL
19004  * <script type="text/javascript">
19005  */
19006  
19007
19008 /**
19009  * @class Roo.tree.ColumnTree
19010  * @extends Roo.data.TreePanel
19011  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19012  * @cfg {int} borderWidth  compined right/left border allowance
19013  * @constructor
19014  * @param {String/HTMLElement/Element} el The container element
19015  * @param {Object} config
19016  */
19017 Roo.tree.ColumnTree =  function(el, config)
19018 {
19019    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19020    this.addEvents({
19021         /**
19022         * @event resize
19023         * Fire this event on a container when it resizes
19024         * @param {int} w Width
19025         * @param {int} h Height
19026         */
19027        "resize" : true
19028     });
19029     this.on('resize', this.onResize, this);
19030 };
19031
19032 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19033     //lines:false,
19034     
19035     
19036     borderWidth: Roo.isBorderBox ? 0 : 2, 
19037     headEls : false,
19038     
19039     render : function(){
19040         // add the header.....
19041        
19042         Roo.tree.ColumnTree.superclass.render.apply(this);
19043         
19044         this.el.addClass('x-column-tree');
19045         
19046         this.headers = this.el.createChild(
19047             {cls:'x-tree-headers'},this.innerCt.dom);
19048    
19049         var cols = this.columns, c;
19050         var totalWidth = 0;
19051         this.headEls = [];
19052         var  len = cols.length;
19053         for(var i = 0; i < len; i++){
19054              c = cols[i];
19055              totalWidth += c.width;
19056             this.headEls.push(this.headers.createChild({
19057                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19058                  cn: {
19059                      cls:'x-tree-hd-text',
19060                      html: c.header
19061                  },
19062                  style:'width:'+(c.width-this.borderWidth)+'px;'
19063              }));
19064         }
19065         this.headers.createChild({cls:'x-clear'});
19066         // prevent floats from wrapping when clipped
19067         this.headers.setWidth(totalWidth);
19068         //this.innerCt.setWidth(totalWidth);
19069         this.innerCt.setStyle({ overflow: 'auto' });
19070         this.onResize(this.width, this.height);
19071              
19072         
19073     },
19074     onResize : function(w,h)
19075     {
19076         this.height = h;
19077         this.width = w;
19078         // resize cols..
19079         this.innerCt.setWidth(this.width);
19080         this.innerCt.setHeight(this.height-20);
19081         
19082         // headers...
19083         var cols = this.columns, c;
19084         var totalWidth = 0;
19085         var expEl = false;
19086         var len = cols.length;
19087         for(var i = 0; i < len; i++){
19088             c = cols[i];
19089             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19090                 // it's the expander..
19091                 expEl  = this.headEls[i];
19092                 continue;
19093             }
19094             totalWidth += c.width;
19095             
19096         }
19097         if (expEl) {
19098             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19099         }
19100         this.headers.setWidth(w-20);
19101
19102         
19103         
19104         
19105     }
19106 });
19107 /*
19108  * Based on:
19109  * Ext JS Library 1.1.1
19110  * Copyright(c) 2006-2007, Ext JS, LLC.
19111  *
19112  * Originally Released Under LGPL - original licence link has changed is not relivant.
19113  *
19114  * Fork - LGPL
19115  * <script type="text/javascript">
19116  */
19117  
19118 /**
19119  * @class Roo.menu.Menu
19120  * @extends Roo.util.Observable
19121  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19122  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19123  * @constructor
19124  * Creates a new Menu
19125  * @param {Object} config Configuration options
19126  */
19127 Roo.menu.Menu = function(config){
19128     Roo.apply(this, config);
19129     this.id = this.id || Roo.id();
19130     this.addEvents({
19131         /**
19132          * @event beforeshow
19133          * Fires before this menu is displayed
19134          * @param {Roo.menu.Menu} this
19135          */
19136         beforeshow : true,
19137         /**
19138          * @event beforehide
19139          * Fires before this menu is hidden
19140          * @param {Roo.menu.Menu} this
19141          */
19142         beforehide : true,
19143         /**
19144          * @event show
19145          * Fires after this menu is displayed
19146          * @param {Roo.menu.Menu} this
19147          */
19148         show : true,
19149         /**
19150          * @event hide
19151          * Fires after this menu is hidden
19152          * @param {Roo.menu.Menu} this
19153          */
19154         hide : true,
19155         /**
19156          * @event click
19157          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19158          * @param {Roo.menu.Menu} this
19159          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19160          * @param {Roo.EventObject} e
19161          */
19162         click : true,
19163         /**
19164          * @event mouseover
19165          * Fires when the mouse is hovering over this menu
19166          * @param {Roo.menu.Menu} this
19167          * @param {Roo.EventObject} e
19168          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19169          */
19170         mouseover : true,
19171         /**
19172          * @event mouseout
19173          * Fires when the mouse exits this menu
19174          * @param {Roo.menu.Menu} this
19175          * @param {Roo.EventObject} e
19176          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19177          */
19178         mouseout : true,
19179         /**
19180          * @event itemclick
19181          * Fires when a menu item contained in this menu is clicked
19182          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19183          * @param {Roo.EventObject} e
19184          */
19185         itemclick: true
19186     });
19187     if (this.registerMenu) {
19188         Roo.menu.MenuMgr.register(this);
19189     }
19190     
19191     var mis = this.items;
19192     this.items = new Roo.util.MixedCollection();
19193     if(mis){
19194         this.add.apply(this, mis);
19195     }
19196 };
19197
19198 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19199     /**
19200      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19201      */
19202     minWidth : 120,
19203     /**
19204      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19205      * for bottom-right shadow (defaults to "sides")
19206      */
19207     shadow : "sides",
19208     /**
19209      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19210      * this menu (defaults to "tl-tr?")
19211      */
19212     subMenuAlign : "tl-tr?",
19213     /**
19214      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19215      * relative to its element of origin (defaults to "tl-bl?")
19216      */
19217     defaultAlign : "tl-bl?",
19218     /**
19219      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19220      */
19221     allowOtherMenus : false,
19222     /**
19223      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19224      */
19225     registerMenu : true,
19226
19227     hidden:true,
19228
19229     // private
19230     render : function(){
19231         if(this.el){
19232             return;
19233         }
19234         var el = this.el = new Roo.Layer({
19235             cls: "x-menu",
19236             shadow:this.shadow,
19237             constrain: false,
19238             parentEl: this.parentEl || document.body,
19239             zindex:15000
19240         });
19241
19242         this.keyNav = new Roo.menu.MenuNav(this);
19243
19244         if(this.plain){
19245             el.addClass("x-menu-plain");
19246         }
19247         if(this.cls){
19248             el.addClass(this.cls);
19249         }
19250         // generic focus element
19251         this.focusEl = el.createChild({
19252             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19253         });
19254         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19255         //disabling touch- as it's causing issues ..
19256         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19257         ul.on('click'   , this.onClick, this);
19258         
19259         
19260         ul.on("mouseover", this.onMouseOver, this);
19261         ul.on("mouseout", this.onMouseOut, this);
19262         this.items.each(function(item){
19263             if (item.hidden) {
19264                 return;
19265             }
19266             
19267             var li = document.createElement("li");
19268             li.className = "x-menu-list-item";
19269             ul.dom.appendChild(li);
19270             item.render(li, this);
19271         }, this);
19272         this.ul = ul;
19273         this.autoWidth();
19274     },
19275
19276     // private
19277     autoWidth : function(){
19278         var el = this.el, ul = this.ul;
19279         if(!el){
19280             return;
19281         }
19282         var w = this.width;
19283         if(w){
19284             el.setWidth(w);
19285         }else if(Roo.isIE){
19286             el.setWidth(this.minWidth);
19287             var t = el.dom.offsetWidth; // force recalc
19288             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19289         }
19290     },
19291
19292     // private
19293     delayAutoWidth : function(){
19294         if(this.rendered){
19295             if(!this.awTask){
19296                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19297             }
19298             this.awTask.delay(20);
19299         }
19300     },
19301
19302     // private
19303     findTargetItem : function(e){
19304         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19305         if(t && t.menuItemId){
19306             return this.items.get(t.menuItemId);
19307         }
19308     },
19309
19310     // private
19311     onClick : function(e){
19312         Roo.log("menu.onClick");
19313         var t = this.findTargetItem(e);
19314         if(!t){
19315             return;
19316         }
19317         Roo.log(e);
19318         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19319             if(t == this.activeItem && t.shouldDeactivate(e)){
19320                 this.activeItem.deactivate();
19321                 delete this.activeItem;
19322                 return;
19323             }
19324             if(t.canActivate){
19325                 this.setActiveItem(t, true);
19326             }
19327             return;
19328             
19329             
19330         }
19331         
19332         t.onClick(e);
19333         this.fireEvent("click", this, t, e);
19334     },
19335
19336     // private
19337     setActiveItem : function(item, autoExpand){
19338         if(item != this.activeItem){
19339             if(this.activeItem){
19340                 this.activeItem.deactivate();
19341             }
19342             this.activeItem = item;
19343             item.activate(autoExpand);
19344         }else if(autoExpand){
19345             item.expandMenu();
19346         }
19347     },
19348
19349     // private
19350     tryActivate : function(start, step){
19351         var items = this.items;
19352         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19353             var item = items.get(i);
19354             if(!item.disabled && item.canActivate){
19355                 this.setActiveItem(item, false);
19356                 return item;
19357             }
19358         }
19359         return false;
19360     },
19361
19362     // private
19363     onMouseOver : function(e){
19364         var t;
19365         if(t = this.findTargetItem(e)){
19366             if(t.canActivate && !t.disabled){
19367                 this.setActiveItem(t, true);
19368             }
19369         }
19370         this.fireEvent("mouseover", this, e, t);
19371     },
19372
19373     // private
19374     onMouseOut : function(e){
19375         var t;
19376         if(t = this.findTargetItem(e)){
19377             if(t == this.activeItem && t.shouldDeactivate(e)){
19378                 this.activeItem.deactivate();
19379                 delete this.activeItem;
19380             }
19381         }
19382         this.fireEvent("mouseout", this, e, t);
19383     },
19384
19385     /**
19386      * Read-only.  Returns true if the menu is currently displayed, else false.
19387      * @type Boolean
19388      */
19389     isVisible : function(){
19390         return this.el && !this.hidden;
19391     },
19392
19393     /**
19394      * Displays this menu relative to another element
19395      * @param {String/HTMLElement/Roo.Element} element The element to align to
19396      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19397      * the element (defaults to this.defaultAlign)
19398      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19399      */
19400     show : function(el, pos, parentMenu){
19401         this.parentMenu = parentMenu;
19402         if(!this.el){
19403             this.render();
19404         }
19405         this.fireEvent("beforeshow", this);
19406         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19407     },
19408
19409     /**
19410      * Displays this menu at a specific xy position
19411      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19412      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19413      */
19414     showAt : function(xy, parentMenu, /* private: */_e){
19415         this.parentMenu = parentMenu;
19416         if(!this.el){
19417             this.render();
19418         }
19419         if(_e !== false){
19420             this.fireEvent("beforeshow", this);
19421             xy = this.el.adjustForConstraints(xy);
19422         }
19423         this.el.setXY(xy);
19424         this.el.show();
19425         this.hidden = false;
19426         this.focus();
19427         this.fireEvent("show", this);
19428     },
19429
19430     focus : function(){
19431         if(!this.hidden){
19432             this.doFocus.defer(50, this);
19433         }
19434     },
19435
19436     doFocus : function(){
19437         if(!this.hidden){
19438             this.focusEl.focus();
19439         }
19440     },
19441
19442     /**
19443      * Hides this menu and optionally all parent menus
19444      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19445      */
19446     hide : function(deep){
19447         if(this.el && this.isVisible()){
19448             this.fireEvent("beforehide", this);
19449             if(this.activeItem){
19450                 this.activeItem.deactivate();
19451                 this.activeItem = null;
19452             }
19453             this.el.hide();
19454             this.hidden = true;
19455             this.fireEvent("hide", this);
19456         }
19457         if(deep === true && this.parentMenu){
19458             this.parentMenu.hide(true);
19459         }
19460     },
19461
19462     /**
19463      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19464      * Any of the following are valid:
19465      * <ul>
19466      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19467      * <li>An HTMLElement object which will be converted to a menu item</li>
19468      * <li>A menu item config object that will be created as a new menu item</li>
19469      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19470      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19471      * </ul>
19472      * Usage:
19473      * <pre><code>
19474 // Create the menu
19475 var menu = new Roo.menu.Menu();
19476
19477 // Create a menu item to add by reference
19478 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19479
19480 // Add a bunch of items at once using different methods.
19481 // Only the last item added will be returned.
19482 var item = menu.add(
19483     menuItem,                // add existing item by ref
19484     'Dynamic Item',          // new TextItem
19485     '-',                     // new separator
19486     { text: 'Config Item' }  // new item by config
19487 );
19488 </code></pre>
19489      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19490      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19491      */
19492     add : function(){
19493         var a = arguments, l = a.length, item;
19494         for(var i = 0; i < l; i++){
19495             var el = a[i];
19496             if ((typeof(el) == "object") && el.xtype && el.xns) {
19497                 el = Roo.factory(el, Roo.menu);
19498             }
19499             
19500             if(el.render){ // some kind of Item
19501                 item = this.addItem(el);
19502             }else if(typeof el == "string"){ // string
19503                 if(el == "separator" || el == "-"){
19504                     item = this.addSeparator();
19505                 }else{
19506                     item = this.addText(el);
19507                 }
19508             }else if(el.tagName || el.el){ // element
19509                 item = this.addElement(el);
19510             }else if(typeof el == "object"){ // must be menu item config?
19511                 item = this.addMenuItem(el);
19512             }
19513         }
19514         return item;
19515     },
19516
19517     /**
19518      * Returns this menu's underlying {@link Roo.Element} object
19519      * @return {Roo.Element} The element
19520      */
19521     getEl : function(){
19522         if(!this.el){
19523             this.render();
19524         }
19525         return this.el;
19526     },
19527
19528     /**
19529      * Adds a separator bar to the menu
19530      * @return {Roo.menu.Item} The menu item that was added
19531      */
19532     addSeparator : function(){
19533         return this.addItem(new Roo.menu.Separator());
19534     },
19535
19536     /**
19537      * Adds an {@link Roo.Element} object to the menu
19538      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19539      * @return {Roo.menu.Item} The menu item that was added
19540      */
19541     addElement : function(el){
19542         return this.addItem(new Roo.menu.BaseItem(el));
19543     },
19544
19545     /**
19546      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19547      * @param {Roo.menu.Item} item The menu item to add
19548      * @return {Roo.menu.Item} The menu item that was added
19549      */
19550     addItem : function(item){
19551         this.items.add(item);
19552         if(this.ul){
19553             var li = document.createElement("li");
19554             li.className = "x-menu-list-item";
19555             this.ul.dom.appendChild(li);
19556             item.render(li, this);
19557             this.delayAutoWidth();
19558         }
19559         return item;
19560     },
19561
19562     /**
19563      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19564      * @param {Object} config A MenuItem config object
19565      * @return {Roo.menu.Item} The menu item that was added
19566      */
19567     addMenuItem : function(config){
19568         if(!(config instanceof Roo.menu.Item)){
19569             if(typeof config.checked == "boolean"){ // must be check menu item config?
19570                 config = new Roo.menu.CheckItem(config);
19571             }else{
19572                 config = new Roo.menu.Item(config);
19573             }
19574         }
19575         return this.addItem(config);
19576     },
19577
19578     /**
19579      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19580      * @param {String} text The text to display in the menu item
19581      * @return {Roo.menu.Item} The menu item that was added
19582      */
19583     addText : function(text){
19584         return this.addItem(new Roo.menu.TextItem({ text : text }));
19585     },
19586
19587     /**
19588      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19589      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19590      * @param {Roo.menu.Item} item The menu item to add
19591      * @return {Roo.menu.Item} The menu item that was added
19592      */
19593     insert : function(index, item){
19594         this.items.insert(index, item);
19595         if(this.ul){
19596             var li = document.createElement("li");
19597             li.className = "x-menu-list-item";
19598             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19599             item.render(li, this);
19600             this.delayAutoWidth();
19601         }
19602         return item;
19603     },
19604
19605     /**
19606      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19607      * @param {Roo.menu.Item} item The menu item to remove
19608      */
19609     remove : function(item){
19610         this.items.removeKey(item.id);
19611         item.destroy();
19612     },
19613
19614     /**
19615      * Removes and destroys all items in the menu
19616      */
19617     removeAll : function(){
19618         var f;
19619         while(f = this.items.first()){
19620             this.remove(f);
19621         }
19622     }
19623 });
19624
19625 // MenuNav is a private utility class used internally by the Menu
19626 Roo.menu.MenuNav = function(menu){
19627     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19628     this.scope = this.menu = menu;
19629 };
19630
19631 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19632     doRelay : function(e, h){
19633         var k = e.getKey();
19634         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19635             this.menu.tryActivate(0, 1);
19636             return false;
19637         }
19638         return h.call(this.scope || this, e, this.menu);
19639     },
19640
19641     up : function(e, m){
19642         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19643             m.tryActivate(m.items.length-1, -1);
19644         }
19645     },
19646
19647     down : function(e, m){
19648         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19649             m.tryActivate(0, 1);
19650         }
19651     },
19652
19653     right : function(e, m){
19654         if(m.activeItem){
19655             m.activeItem.expandMenu(true);
19656         }
19657     },
19658
19659     left : function(e, m){
19660         m.hide();
19661         if(m.parentMenu && m.parentMenu.activeItem){
19662             m.parentMenu.activeItem.activate();
19663         }
19664     },
19665
19666     enter : function(e, m){
19667         if(m.activeItem){
19668             e.stopPropagation();
19669             m.activeItem.onClick(e);
19670             m.fireEvent("click", this, m.activeItem);
19671             return true;
19672         }
19673     }
19674 });/*
19675  * Based on:
19676  * Ext JS Library 1.1.1
19677  * Copyright(c) 2006-2007, Ext JS, LLC.
19678  *
19679  * Originally Released Under LGPL - original licence link has changed is not relivant.
19680  *
19681  * Fork - LGPL
19682  * <script type="text/javascript">
19683  */
19684  
19685 /**
19686  * @class Roo.menu.MenuMgr
19687  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19688  * @singleton
19689  */
19690 Roo.menu.MenuMgr = function(){
19691    var menus, active, groups = {}, attached = false, lastShow = new Date();
19692
19693    // private - called when first menu is created
19694    function init(){
19695        menus = {};
19696        active = new Roo.util.MixedCollection();
19697        Roo.get(document).addKeyListener(27, function(){
19698            if(active.length > 0){
19699                hideAll();
19700            }
19701        });
19702    }
19703
19704    // private
19705    function hideAll(){
19706        if(active && active.length > 0){
19707            var c = active.clone();
19708            c.each(function(m){
19709                m.hide();
19710            });
19711        }
19712    }
19713
19714    // private
19715    function onHide(m){
19716        active.remove(m);
19717        if(active.length < 1){
19718            Roo.get(document).un("mousedown", onMouseDown);
19719            attached = false;
19720        }
19721    }
19722
19723    // private
19724    function onShow(m){
19725        var last = active.last();
19726        lastShow = new Date();
19727        active.add(m);
19728        if(!attached){
19729            Roo.get(document).on("mousedown", onMouseDown);
19730            attached = true;
19731        }
19732        if(m.parentMenu){
19733           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19734           m.parentMenu.activeChild = m;
19735        }else if(last && last.isVisible()){
19736           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19737        }
19738    }
19739
19740    // private
19741    function onBeforeHide(m){
19742        if(m.activeChild){
19743            m.activeChild.hide();
19744        }
19745        if(m.autoHideTimer){
19746            clearTimeout(m.autoHideTimer);
19747            delete m.autoHideTimer;
19748        }
19749    }
19750
19751    // private
19752    function onBeforeShow(m){
19753        var pm = m.parentMenu;
19754        if(!pm && !m.allowOtherMenus){
19755            hideAll();
19756        }else if(pm && pm.activeChild && active != m){
19757            pm.activeChild.hide();
19758        }
19759    }
19760
19761    // private
19762    function onMouseDown(e){
19763        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19764            hideAll();
19765        }
19766    }
19767
19768    // private
19769    function onBeforeCheck(mi, state){
19770        if(state){
19771            var g = groups[mi.group];
19772            for(var i = 0, l = g.length; i < l; i++){
19773                if(g[i] != mi){
19774                    g[i].setChecked(false);
19775                }
19776            }
19777        }
19778    }
19779
19780    return {
19781
19782        /**
19783         * Hides all menus that are currently visible
19784         */
19785        hideAll : function(){
19786             hideAll();  
19787        },
19788
19789        // private
19790        register : function(menu){
19791            if(!menus){
19792                init();
19793            }
19794            menus[menu.id] = menu;
19795            menu.on("beforehide", onBeforeHide);
19796            menu.on("hide", onHide);
19797            menu.on("beforeshow", onBeforeShow);
19798            menu.on("show", onShow);
19799            var g = menu.group;
19800            if(g && menu.events["checkchange"]){
19801                if(!groups[g]){
19802                    groups[g] = [];
19803                }
19804                groups[g].push(menu);
19805                menu.on("checkchange", onCheck);
19806            }
19807        },
19808
19809         /**
19810          * Returns a {@link Roo.menu.Menu} object
19811          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19812          * be used to generate and return a new Menu instance.
19813          */
19814        get : function(menu){
19815            if(typeof menu == "string"){ // menu id
19816                return menus[menu];
19817            }else if(menu.events){  // menu instance
19818                return menu;
19819            }else if(typeof menu.length == 'number'){ // array of menu items?
19820                return new Roo.menu.Menu({items:menu});
19821            }else{ // otherwise, must be a config
19822                return new Roo.menu.Menu(menu);
19823            }
19824        },
19825
19826        // private
19827        unregister : function(menu){
19828            delete menus[menu.id];
19829            menu.un("beforehide", onBeforeHide);
19830            menu.un("hide", onHide);
19831            menu.un("beforeshow", onBeforeShow);
19832            menu.un("show", onShow);
19833            var g = menu.group;
19834            if(g && menu.events["checkchange"]){
19835                groups[g].remove(menu);
19836                menu.un("checkchange", onCheck);
19837            }
19838        },
19839
19840        // private
19841        registerCheckable : function(menuItem){
19842            var g = menuItem.group;
19843            if(g){
19844                if(!groups[g]){
19845                    groups[g] = [];
19846                }
19847                groups[g].push(menuItem);
19848                menuItem.on("beforecheckchange", onBeforeCheck);
19849            }
19850        },
19851
19852        // private
19853        unregisterCheckable : function(menuItem){
19854            var g = menuItem.group;
19855            if(g){
19856                groups[g].remove(menuItem);
19857                menuItem.un("beforecheckchange", onBeforeCheck);
19858            }
19859        }
19860    };
19861 }();/*
19862  * Based on:
19863  * Ext JS Library 1.1.1
19864  * Copyright(c) 2006-2007, Ext JS, LLC.
19865  *
19866  * Originally Released Under LGPL - original licence link has changed is not relivant.
19867  *
19868  * Fork - LGPL
19869  * <script type="text/javascript">
19870  */
19871  
19872
19873 /**
19874  * @class Roo.menu.BaseItem
19875  * @extends Roo.Component
19876  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19877  * management and base configuration options shared by all menu components.
19878  * @constructor
19879  * Creates a new BaseItem
19880  * @param {Object} config Configuration options
19881  */
19882 Roo.menu.BaseItem = function(config){
19883     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19884
19885     this.addEvents({
19886         /**
19887          * @event click
19888          * Fires when this item is clicked
19889          * @param {Roo.menu.BaseItem} this
19890          * @param {Roo.EventObject} e
19891          */
19892         click: true,
19893         /**
19894          * @event activate
19895          * Fires when this item is activated
19896          * @param {Roo.menu.BaseItem} this
19897          */
19898         activate : true,
19899         /**
19900          * @event deactivate
19901          * Fires when this item is deactivated
19902          * @param {Roo.menu.BaseItem} this
19903          */
19904         deactivate : true
19905     });
19906
19907     if(this.handler){
19908         this.on("click", this.handler, this.scope, true);
19909     }
19910 };
19911
19912 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19913     /**
19914      * @cfg {Function} handler
19915      * A function that will handle the click event of this menu item (defaults to undefined)
19916      */
19917     /**
19918      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19919      */
19920     canActivate : false,
19921     
19922      /**
19923      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19924      */
19925     hidden: false,
19926     
19927     /**
19928      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19929      */
19930     activeClass : "x-menu-item-active",
19931     /**
19932      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19933      */
19934     hideOnClick : true,
19935     /**
19936      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19937      */
19938     hideDelay : 100,
19939
19940     // private
19941     ctype: "Roo.menu.BaseItem",
19942
19943     // private
19944     actionMode : "container",
19945
19946     // private
19947     render : function(container, parentMenu){
19948         this.parentMenu = parentMenu;
19949         Roo.menu.BaseItem.superclass.render.call(this, container);
19950         this.container.menuItemId = this.id;
19951     },
19952
19953     // private
19954     onRender : function(container, position){
19955         this.el = Roo.get(this.el);
19956         container.dom.appendChild(this.el.dom);
19957     },
19958
19959     // private
19960     onClick : function(e){
19961         if(!this.disabled && this.fireEvent("click", this, e) !== false
19962                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19963             this.handleClick(e);
19964         }else{
19965             e.stopEvent();
19966         }
19967     },
19968
19969     // private
19970     activate : function(){
19971         if(this.disabled){
19972             return false;
19973         }
19974         var li = this.container;
19975         li.addClass(this.activeClass);
19976         this.region = li.getRegion().adjust(2, 2, -2, -2);
19977         this.fireEvent("activate", this);
19978         return true;
19979     },
19980
19981     // private
19982     deactivate : function(){
19983         this.container.removeClass(this.activeClass);
19984         this.fireEvent("deactivate", this);
19985     },
19986
19987     // private
19988     shouldDeactivate : function(e){
19989         return !this.region || !this.region.contains(e.getPoint());
19990     },
19991
19992     // private
19993     handleClick : function(e){
19994         if(this.hideOnClick){
19995             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19996         }
19997     },
19998
19999     // private
20000     expandMenu : function(autoActivate){
20001         // do nothing
20002     },
20003
20004     // private
20005     hideMenu : function(){
20006         // do nothing
20007     }
20008 });/*
20009  * Based on:
20010  * Ext JS Library 1.1.1
20011  * Copyright(c) 2006-2007, Ext JS, LLC.
20012  *
20013  * Originally Released Under LGPL - original licence link has changed is not relivant.
20014  *
20015  * Fork - LGPL
20016  * <script type="text/javascript">
20017  */
20018  
20019 /**
20020  * @class Roo.menu.Adapter
20021  * @extends Roo.menu.BaseItem
20022  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20023  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20024  * @constructor
20025  * Creates a new Adapter
20026  * @param {Object} config Configuration options
20027  */
20028 Roo.menu.Adapter = function(component, config){
20029     Roo.menu.Adapter.superclass.constructor.call(this, config);
20030     this.component = component;
20031 };
20032 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20033     // private
20034     canActivate : true,
20035
20036     // private
20037     onRender : function(container, position){
20038         this.component.render(container);
20039         this.el = this.component.getEl();
20040     },
20041
20042     // private
20043     activate : function(){
20044         if(this.disabled){
20045             return false;
20046         }
20047         this.component.focus();
20048         this.fireEvent("activate", this);
20049         return true;
20050     },
20051
20052     // private
20053     deactivate : function(){
20054         this.fireEvent("deactivate", this);
20055     },
20056
20057     // private
20058     disable : function(){
20059         this.component.disable();
20060         Roo.menu.Adapter.superclass.disable.call(this);
20061     },
20062
20063     // private
20064     enable : function(){
20065         this.component.enable();
20066         Roo.menu.Adapter.superclass.enable.call(this);
20067     }
20068 });/*
20069  * Based on:
20070  * Ext JS Library 1.1.1
20071  * Copyright(c) 2006-2007, Ext JS, LLC.
20072  *
20073  * Originally Released Under LGPL - original licence link has changed is not relivant.
20074  *
20075  * Fork - LGPL
20076  * <script type="text/javascript">
20077  */
20078
20079 /**
20080  * @class Roo.menu.TextItem
20081  * @extends Roo.menu.BaseItem
20082  * Adds a static text string to a menu, usually used as either a heading or group separator.
20083  * Note: old style constructor with text is still supported.
20084  * 
20085  * @constructor
20086  * Creates a new TextItem
20087  * @param {Object} cfg Configuration
20088  */
20089 Roo.menu.TextItem = function(cfg){
20090     if (typeof(cfg) == 'string') {
20091         this.text = cfg;
20092     } else {
20093         Roo.apply(this,cfg);
20094     }
20095     
20096     Roo.menu.TextItem.superclass.constructor.call(this);
20097 };
20098
20099 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20100     /**
20101      * @cfg {Boolean} text Text to show on item.
20102      */
20103     text : '',
20104     
20105     /**
20106      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20107      */
20108     hideOnClick : false,
20109     /**
20110      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20111      */
20112     itemCls : "x-menu-text",
20113
20114     // private
20115     onRender : function(){
20116         var s = document.createElement("span");
20117         s.className = this.itemCls;
20118         s.innerHTML = this.text;
20119         this.el = s;
20120         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20121     }
20122 });/*
20123  * Based on:
20124  * Ext JS Library 1.1.1
20125  * Copyright(c) 2006-2007, Ext JS, LLC.
20126  *
20127  * Originally Released Under LGPL - original licence link has changed is not relivant.
20128  *
20129  * Fork - LGPL
20130  * <script type="text/javascript">
20131  */
20132
20133 /**
20134  * @class Roo.menu.Separator
20135  * @extends Roo.menu.BaseItem
20136  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20137  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20138  * @constructor
20139  * @param {Object} config Configuration options
20140  */
20141 Roo.menu.Separator = function(config){
20142     Roo.menu.Separator.superclass.constructor.call(this, config);
20143 };
20144
20145 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20146     /**
20147      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20148      */
20149     itemCls : "x-menu-sep",
20150     /**
20151      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20152      */
20153     hideOnClick : false,
20154
20155     // private
20156     onRender : function(li){
20157         var s = document.createElement("span");
20158         s.className = this.itemCls;
20159         s.innerHTML = "&#160;";
20160         this.el = s;
20161         li.addClass("x-menu-sep-li");
20162         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20163     }
20164 });/*
20165  * Based on:
20166  * Ext JS Library 1.1.1
20167  * Copyright(c) 2006-2007, Ext JS, LLC.
20168  *
20169  * Originally Released Under LGPL - original licence link has changed is not relivant.
20170  *
20171  * Fork - LGPL
20172  * <script type="text/javascript">
20173  */
20174 /**
20175  * @class Roo.menu.Item
20176  * @extends Roo.menu.BaseItem
20177  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20178  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20179  * activation and click handling.
20180  * @constructor
20181  * Creates a new Item
20182  * @param {Object} config Configuration options
20183  */
20184 Roo.menu.Item = function(config){
20185     Roo.menu.Item.superclass.constructor.call(this, config);
20186     if(this.menu){
20187         this.menu = Roo.menu.MenuMgr.get(this.menu);
20188     }
20189 };
20190 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20191     
20192     /**
20193      * @cfg {String} text
20194      * The text to show on the menu item.
20195      */
20196     text: '',
20197      /**
20198      * @cfg {String} HTML to render in menu
20199      * The text to show on the menu item (HTML version).
20200      */
20201     html: '',
20202     /**
20203      * @cfg {String} icon
20204      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20205      */
20206     icon: undefined,
20207     /**
20208      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20209      */
20210     itemCls : "x-menu-item",
20211     /**
20212      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20213      */
20214     canActivate : true,
20215     /**
20216      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20217      */
20218     showDelay: 200,
20219     // doc'd in BaseItem
20220     hideDelay: 200,
20221
20222     // private
20223     ctype: "Roo.menu.Item",
20224     
20225     // private
20226     onRender : function(container, position){
20227         var el = document.createElement("a");
20228         el.hideFocus = true;
20229         el.unselectable = "on";
20230         el.href = this.href || "#";
20231         if(this.hrefTarget){
20232             el.target = this.hrefTarget;
20233         }
20234         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20235         
20236         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20237         
20238         el.innerHTML = String.format(
20239                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20240                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20241         this.el = el;
20242         Roo.menu.Item.superclass.onRender.call(this, container, position);
20243     },
20244
20245     /**
20246      * Sets the text to display in this menu item
20247      * @param {String} text The text to display
20248      * @param {Boolean} isHTML true to indicate text is pure html.
20249      */
20250     setText : function(text, isHTML){
20251         if (isHTML) {
20252             this.html = text;
20253         } else {
20254             this.text = text;
20255             this.html = '';
20256         }
20257         if(this.rendered){
20258             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20259      
20260             this.el.update(String.format(
20261                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20262                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20263             this.parentMenu.autoWidth();
20264         }
20265     },
20266
20267     // private
20268     handleClick : function(e){
20269         if(!this.href){ // if no link defined, stop the event automatically
20270             e.stopEvent();
20271         }
20272         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20273     },
20274
20275     // private
20276     activate : function(autoExpand){
20277         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20278             this.focus();
20279             if(autoExpand){
20280                 this.expandMenu();
20281             }
20282         }
20283         return true;
20284     },
20285
20286     // private
20287     shouldDeactivate : function(e){
20288         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20289             if(this.menu && this.menu.isVisible()){
20290                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20291             }
20292             return true;
20293         }
20294         return false;
20295     },
20296
20297     // private
20298     deactivate : function(){
20299         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20300         this.hideMenu();
20301     },
20302
20303     // private
20304     expandMenu : function(autoActivate){
20305         if(!this.disabled && this.menu){
20306             clearTimeout(this.hideTimer);
20307             delete this.hideTimer;
20308             if(!this.menu.isVisible() && !this.showTimer){
20309                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20310             }else if (this.menu.isVisible() && autoActivate){
20311                 this.menu.tryActivate(0, 1);
20312             }
20313         }
20314     },
20315
20316     // private
20317     deferExpand : function(autoActivate){
20318         delete this.showTimer;
20319         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20320         if(autoActivate){
20321             this.menu.tryActivate(0, 1);
20322         }
20323     },
20324
20325     // private
20326     hideMenu : function(){
20327         clearTimeout(this.showTimer);
20328         delete this.showTimer;
20329         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20330             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20331         }
20332     },
20333
20334     // private
20335     deferHide : function(){
20336         delete this.hideTimer;
20337         this.menu.hide();
20338     }
20339 });/*
20340  * Based on:
20341  * Ext JS Library 1.1.1
20342  * Copyright(c) 2006-2007, Ext JS, LLC.
20343  *
20344  * Originally Released Under LGPL - original licence link has changed is not relivant.
20345  *
20346  * Fork - LGPL
20347  * <script type="text/javascript">
20348  */
20349  
20350 /**
20351  * @class Roo.menu.CheckItem
20352  * @extends Roo.menu.Item
20353  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20354  * @constructor
20355  * Creates a new CheckItem
20356  * @param {Object} config Configuration options
20357  */
20358 Roo.menu.CheckItem = function(config){
20359     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20360     this.addEvents({
20361         /**
20362          * @event beforecheckchange
20363          * Fires before the checked value is set, providing an opportunity to cancel if needed
20364          * @param {Roo.menu.CheckItem} this
20365          * @param {Boolean} checked The new checked value that will be set
20366          */
20367         "beforecheckchange" : true,
20368         /**
20369          * @event checkchange
20370          * Fires after the checked value has been set
20371          * @param {Roo.menu.CheckItem} this
20372          * @param {Boolean} checked The checked value that was set
20373          */
20374         "checkchange" : true
20375     });
20376     if(this.checkHandler){
20377         this.on('checkchange', this.checkHandler, this.scope);
20378     }
20379 };
20380 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20381     /**
20382      * @cfg {String} group
20383      * All check items with the same group name will automatically be grouped into a single-select
20384      * radio button group (defaults to '')
20385      */
20386     /**
20387      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20388      */
20389     itemCls : "x-menu-item x-menu-check-item",
20390     /**
20391      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20392      */
20393     groupClass : "x-menu-group-item",
20394
20395     /**
20396      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20397      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20398      * initialized with checked = true will be rendered as checked.
20399      */
20400     checked: false,
20401
20402     // private
20403     ctype: "Roo.menu.CheckItem",
20404
20405     // private
20406     onRender : function(c){
20407         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20408         if(this.group){
20409             this.el.addClass(this.groupClass);
20410         }
20411         Roo.menu.MenuMgr.registerCheckable(this);
20412         if(this.checked){
20413             this.checked = false;
20414             this.setChecked(true, true);
20415         }
20416     },
20417
20418     // private
20419     destroy : function(){
20420         if(this.rendered){
20421             Roo.menu.MenuMgr.unregisterCheckable(this);
20422         }
20423         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20424     },
20425
20426     /**
20427      * Set the checked state of this item
20428      * @param {Boolean} checked The new checked value
20429      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20430      */
20431     setChecked : function(state, suppressEvent){
20432         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20433             if(this.container){
20434                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20435             }
20436             this.checked = state;
20437             if(suppressEvent !== true){
20438                 this.fireEvent("checkchange", this, state);
20439             }
20440         }
20441     },
20442
20443     // private
20444     handleClick : function(e){
20445        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20446            this.setChecked(!this.checked);
20447        }
20448        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20449     }
20450 });/*
20451  * Based on:
20452  * Ext JS Library 1.1.1
20453  * Copyright(c) 2006-2007, Ext JS, LLC.
20454  *
20455  * Originally Released Under LGPL - original licence link has changed is not relivant.
20456  *
20457  * Fork - LGPL
20458  * <script type="text/javascript">
20459  */
20460  
20461 /**
20462  * @class Roo.menu.DateItem
20463  * @extends Roo.menu.Adapter
20464  * A menu item that wraps the {@link Roo.DatPicker} component.
20465  * @constructor
20466  * Creates a new DateItem
20467  * @param {Object} config Configuration options
20468  */
20469 Roo.menu.DateItem = function(config){
20470     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20471     /** The Roo.DatePicker object @type Roo.DatePicker */
20472     this.picker = this.component;
20473     this.addEvents({select: true});
20474     
20475     this.picker.on("render", function(picker){
20476         picker.getEl().swallowEvent("click");
20477         picker.container.addClass("x-menu-date-item");
20478     });
20479
20480     this.picker.on("select", this.onSelect, this);
20481 };
20482
20483 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20484     // private
20485     onSelect : function(picker, date){
20486         this.fireEvent("select", this, date, picker);
20487         Roo.menu.DateItem.superclass.handleClick.call(this);
20488     }
20489 });/*
20490  * Based on:
20491  * Ext JS Library 1.1.1
20492  * Copyright(c) 2006-2007, Ext JS, LLC.
20493  *
20494  * Originally Released Under LGPL - original licence link has changed is not relivant.
20495  *
20496  * Fork - LGPL
20497  * <script type="text/javascript">
20498  */
20499  
20500 /**
20501  * @class Roo.menu.ColorItem
20502  * @extends Roo.menu.Adapter
20503  * A menu item that wraps the {@link Roo.ColorPalette} component.
20504  * @constructor
20505  * Creates a new ColorItem
20506  * @param {Object} config Configuration options
20507  */
20508 Roo.menu.ColorItem = function(config){
20509     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20510     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20511     this.palette = this.component;
20512     this.relayEvents(this.palette, ["select"]);
20513     if(this.selectHandler){
20514         this.on('select', this.selectHandler, this.scope);
20515     }
20516 };
20517 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20518  * Based on:
20519  * Ext JS Library 1.1.1
20520  * Copyright(c) 2006-2007, Ext JS, LLC.
20521  *
20522  * Originally Released Under LGPL - original licence link has changed is not relivant.
20523  *
20524  * Fork - LGPL
20525  * <script type="text/javascript">
20526  */
20527  
20528
20529 /**
20530  * @class Roo.menu.DateMenu
20531  * @extends Roo.menu.Menu
20532  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20533  * @constructor
20534  * Creates a new DateMenu
20535  * @param {Object} config Configuration options
20536  */
20537 Roo.menu.DateMenu = function(config){
20538     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20539     this.plain = true;
20540     var di = new Roo.menu.DateItem(config);
20541     this.add(di);
20542     /**
20543      * The {@link Roo.DatePicker} instance for this DateMenu
20544      * @type DatePicker
20545      */
20546     this.picker = di.picker;
20547     /**
20548      * @event select
20549      * @param {DatePicker} picker
20550      * @param {Date} date
20551      */
20552     this.relayEvents(di, ["select"]);
20553     this.on('beforeshow', function(){
20554         if(this.picker){
20555             this.picker.hideMonthPicker(false);
20556         }
20557     }, this);
20558 };
20559 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20560     cls:'x-date-menu'
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571  
20572
20573 /**
20574  * @class Roo.menu.ColorMenu
20575  * @extends Roo.menu.Menu
20576  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20577  * @constructor
20578  * Creates a new ColorMenu
20579  * @param {Object} config Configuration options
20580  */
20581 Roo.menu.ColorMenu = function(config){
20582     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20583     this.plain = true;
20584     var ci = new Roo.menu.ColorItem(config);
20585     this.add(ci);
20586     /**
20587      * The {@link Roo.ColorPalette} instance for this ColorMenu
20588      * @type ColorPalette
20589      */
20590     this.palette = ci.palette;
20591     /**
20592      * @event select
20593      * @param {ColorPalette} palette
20594      * @param {String} color
20595      */
20596     this.relayEvents(ci, ["select"]);
20597 };
20598 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20599  * Based on:
20600  * Ext JS Library 1.1.1
20601  * Copyright(c) 2006-2007, Ext JS, LLC.
20602  *
20603  * Originally Released Under LGPL - original licence link has changed is not relivant.
20604  *
20605  * Fork - LGPL
20606  * <script type="text/javascript">
20607  */
20608  
20609 /**
20610  * @class Roo.form.Field
20611  * @extends Roo.BoxComponent
20612  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20613  * @constructor
20614  * Creates a new Field
20615  * @param {Object} config Configuration options
20616  */
20617 Roo.form.Field = function(config){
20618     Roo.form.Field.superclass.constructor.call(this, config);
20619 };
20620
20621 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20622     /**
20623      * @cfg {String} fieldLabel Label to use when rendering a form.
20624      */
20625        /**
20626      * @cfg {String} qtip Mouse over tip
20627      */
20628      
20629     /**
20630      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20631      */
20632     invalidClass : "x-form-invalid",
20633     /**
20634      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20635      */
20636     invalidText : "The value in this field is invalid",
20637     /**
20638      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20639      */
20640     focusClass : "x-form-focus",
20641     /**
20642      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20643       automatic validation (defaults to "keyup").
20644      */
20645     validationEvent : "keyup",
20646     /**
20647      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20648      */
20649     validateOnBlur : true,
20650     /**
20651      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20652      */
20653     validationDelay : 250,
20654     /**
20655      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20656      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20657      */
20658     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20659     /**
20660      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20661      */
20662     fieldClass : "x-form-field",
20663     /**
20664      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20665      *<pre>
20666 Value         Description
20667 -----------   ----------------------------------------------------------------------
20668 qtip          Display a quick tip when the user hovers over the field
20669 title         Display a default browser title attribute popup
20670 under         Add a block div beneath the field containing the error text
20671 side          Add an error icon to the right of the field with a popup on hover
20672 [element id]  Add the error text directly to the innerHTML of the specified element
20673 </pre>
20674      */
20675     msgTarget : 'qtip',
20676     /**
20677      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20678      */
20679     msgFx : 'normal',
20680
20681     /**
20682      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20683      */
20684     readOnly : false,
20685
20686     /**
20687      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20688      */
20689     disabled : false,
20690
20691     /**
20692      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20693      */
20694     inputType : undefined,
20695     
20696     /**
20697      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20698          */
20699         tabIndex : undefined,
20700         
20701     // private
20702     isFormField : true,
20703
20704     // private
20705     hasFocus : false,
20706     /**
20707      * @property {Roo.Element} fieldEl
20708      * Element Containing the rendered Field (with label etc.)
20709      */
20710     /**
20711      * @cfg {Mixed} value A value to initialize this field with.
20712      */
20713     value : undefined,
20714
20715     /**
20716      * @cfg {String} name The field's HTML name attribute.
20717      */
20718     /**
20719      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20720      */
20721
20722         // private ??
20723         initComponent : function(){
20724         Roo.form.Field.superclass.initComponent.call(this);
20725         this.addEvents({
20726             /**
20727              * @event focus
20728              * Fires when this field receives input focus.
20729              * @param {Roo.form.Field} this
20730              */
20731             focus : true,
20732             /**
20733              * @event blur
20734              * Fires when this field loses input focus.
20735              * @param {Roo.form.Field} this
20736              */
20737             blur : true,
20738             /**
20739              * @event specialkey
20740              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20741              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20742              * @param {Roo.form.Field} this
20743              * @param {Roo.EventObject} e The event object
20744              */
20745             specialkey : true,
20746             /**
20747              * @event change
20748              * Fires just before the field blurs if the field value has changed.
20749              * @param {Roo.form.Field} this
20750              * @param {Mixed} newValue The new value
20751              * @param {Mixed} oldValue The original value
20752              */
20753             change : true,
20754             /**
20755              * @event invalid
20756              * Fires after the field has been marked as invalid.
20757              * @param {Roo.form.Field} this
20758              * @param {String} msg The validation message
20759              */
20760             invalid : true,
20761             /**
20762              * @event valid
20763              * Fires after the field has been validated with no errors.
20764              * @param {Roo.form.Field} this
20765              */
20766             valid : true,
20767              /**
20768              * @event keyup
20769              * Fires after the key up
20770              * @param {Roo.form.Field} this
20771              * @param {Roo.EventObject}  e The event Object
20772              */
20773             keyup : true
20774         });
20775     },
20776
20777     /**
20778      * Returns the name attribute of the field if available
20779      * @return {String} name The field name
20780      */
20781     getName: function(){
20782          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20783     },
20784
20785     // private
20786     onRender : function(ct, position){
20787         Roo.form.Field.superclass.onRender.call(this, ct, position);
20788         if(!this.el){
20789             var cfg = this.getAutoCreate();
20790             if(!cfg.name){
20791                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20792             }
20793             if (!cfg.name.length) {
20794                 delete cfg.name;
20795             }
20796             if(this.inputType){
20797                 cfg.type = this.inputType;
20798             }
20799             this.el = ct.createChild(cfg, position);
20800         }
20801         var type = this.el.dom.type;
20802         if(type){
20803             if(type == 'password'){
20804                 type = 'text';
20805             }
20806             this.el.addClass('x-form-'+type);
20807         }
20808         if(this.readOnly){
20809             this.el.dom.readOnly = true;
20810         }
20811         if(this.tabIndex !== undefined){
20812             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20813         }
20814
20815         this.el.addClass([this.fieldClass, this.cls]);
20816         this.initValue();
20817     },
20818
20819     /**
20820      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20821      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20822      * @return {Roo.form.Field} this
20823      */
20824     applyTo : function(target){
20825         this.allowDomMove = false;
20826         this.el = Roo.get(target);
20827         this.render(this.el.dom.parentNode);
20828         return this;
20829     },
20830
20831     // private
20832     initValue : function(){
20833         if(this.value !== undefined){
20834             this.setValue(this.value);
20835         }else if(this.el.dom.value.length > 0){
20836             this.setValue(this.el.dom.value);
20837         }
20838     },
20839
20840     /**
20841      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20842      */
20843     isDirty : function() {
20844         if(this.disabled) {
20845             return false;
20846         }
20847         return String(this.getValue()) !== String(this.originalValue);
20848     },
20849
20850     // private
20851     afterRender : function(){
20852         Roo.form.Field.superclass.afterRender.call(this);
20853         this.initEvents();
20854     },
20855
20856     // private
20857     fireKey : function(e){
20858         //Roo.log('field ' + e.getKey());
20859         if(e.isNavKeyPress()){
20860             this.fireEvent("specialkey", this, e);
20861         }
20862     },
20863
20864     /**
20865      * Resets the current field value to the originally loaded value and clears any validation messages
20866      */
20867     reset : function(){
20868         this.setValue(this.resetValue);
20869         this.clearInvalid();
20870     },
20871
20872     // private
20873     initEvents : function(){
20874         // safari killled keypress - so keydown is now used..
20875         this.el.on("keydown" , this.fireKey,  this);
20876         this.el.on("focus", this.onFocus,  this);
20877         this.el.on("blur", this.onBlur,  this);
20878         this.el.relayEvent('keyup', this);
20879
20880         // reference to original value for reset
20881         this.originalValue = this.getValue();
20882         this.resetValue =  this.getValue();
20883     },
20884
20885     // private
20886     onFocus : function(){
20887         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20888             this.el.addClass(this.focusClass);
20889         }
20890         if(!this.hasFocus){
20891             this.hasFocus = true;
20892             this.startValue = this.getValue();
20893             this.fireEvent("focus", this);
20894         }
20895     },
20896
20897     beforeBlur : Roo.emptyFn,
20898
20899     // private
20900     onBlur : function(){
20901         this.beforeBlur();
20902         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20903             this.el.removeClass(this.focusClass);
20904         }
20905         this.hasFocus = false;
20906         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20907             this.validate();
20908         }
20909         var v = this.getValue();
20910         if(String(v) !== String(this.startValue)){
20911             this.fireEvent('change', this, v, this.startValue);
20912         }
20913         this.fireEvent("blur", this);
20914     },
20915
20916     /**
20917      * Returns whether or not the field value is currently valid
20918      * @param {Boolean} preventMark True to disable marking the field invalid
20919      * @return {Boolean} True if the value is valid, else false
20920      */
20921     isValid : function(preventMark){
20922         if(this.disabled){
20923             return true;
20924         }
20925         var restore = this.preventMark;
20926         this.preventMark = preventMark === true;
20927         var v = this.validateValue(this.processValue(this.getRawValue()));
20928         this.preventMark = restore;
20929         return v;
20930     },
20931
20932     /**
20933      * Validates the field value
20934      * @return {Boolean} True if the value is valid, else false
20935      */
20936     validate : function(){
20937         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20938             this.clearInvalid();
20939             return true;
20940         }
20941         return false;
20942     },
20943
20944     processValue : function(value){
20945         return value;
20946     },
20947
20948     // private
20949     // Subclasses should provide the validation implementation by overriding this
20950     validateValue : function(value){
20951         return true;
20952     },
20953
20954     /**
20955      * Mark this field as invalid
20956      * @param {String} msg The validation message
20957      */
20958     markInvalid : function(msg){
20959         if(!this.rendered || this.preventMark){ // not rendered
20960             return;
20961         }
20962         
20963         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20964         
20965         obj.el.addClass(this.invalidClass);
20966         msg = msg || this.invalidText;
20967         switch(this.msgTarget){
20968             case 'qtip':
20969                 obj.el.dom.qtip = msg;
20970                 obj.el.dom.qclass = 'x-form-invalid-tip';
20971                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20972                     Roo.QuickTips.enable();
20973                 }
20974                 break;
20975             case 'title':
20976                 this.el.dom.title = msg;
20977                 break;
20978             case 'under':
20979                 if(!this.errorEl){
20980                     var elp = this.el.findParent('.x-form-element', 5, true);
20981                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20982                     this.errorEl.setWidth(elp.getWidth(true)-20);
20983                 }
20984                 this.errorEl.update(msg);
20985                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20986                 break;
20987             case 'side':
20988                 if(!this.errorIcon){
20989                     var elp = this.el.findParent('.x-form-element', 5, true);
20990                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20991                 }
20992                 this.alignErrorIcon();
20993                 this.errorIcon.dom.qtip = msg;
20994                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20995                 this.errorIcon.show();
20996                 this.on('resize', this.alignErrorIcon, this);
20997                 break;
20998             default:
20999                 var t = Roo.getDom(this.msgTarget);
21000                 t.innerHTML = msg;
21001                 t.style.display = this.msgDisplay;
21002                 break;
21003         }
21004         this.fireEvent('invalid', this, msg);
21005     },
21006
21007     // private
21008     alignErrorIcon : function(){
21009         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21010     },
21011
21012     /**
21013      * Clear any invalid styles/messages for this field
21014      */
21015     clearInvalid : function(){
21016         if(!this.rendered || this.preventMark){ // not rendered
21017             return;
21018         }
21019         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21020         
21021         obj.el.removeClass(this.invalidClass);
21022         switch(this.msgTarget){
21023             case 'qtip':
21024                 obj.el.dom.qtip = '';
21025                 break;
21026             case 'title':
21027                 this.el.dom.title = '';
21028                 break;
21029             case 'under':
21030                 if(this.errorEl){
21031                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21032                 }
21033                 break;
21034             case 'side':
21035                 if(this.errorIcon){
21036                     this.errorIcon.dom.qtip = '';
21037                     this.errorIcon.hide();
21038                     this.un('resize', this.alignErrorIcon, this);
21039                 }
21040                 break;
21041             default:
21042                 var t = Roo.getDom(this.msgTarget);
21043                 t.innerHTML = '';
21044                 t.style.display = 'none';
21045                 break;
21046         }
21047         this.fireEvent('valid', this);
21048     },
21049
21050     /**
21051      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21052      * @return {Mixed} value The field value
21053      */
21054     getRawValue : function(){
21055         var v = this.el.getValue();
21056         
21057         return v;
21058     },
21059
21060     /**
21061      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21062      * @return {Mixed} value The field value
21063      */
21064     getValue : function(){
21065         var v = this.el.getValue();
21066          
21067         return v;
21068     },
21069
21070     /**
21071      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21072      * @param {Mixed} value The value to set
21073      */
21074     setRawValue : function(v){
21075         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21076     },
21077
21078     /**
21079      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21080      * @param {Mixed} value The value to set
21081      */
21082     setValue : function(v){
21083         this.value = v;
21084         if(this.rendered){
21085             this.el.dom.value = (v === null || v === undefined ? '' : v);
21086              this.validate();
21087         }
21088     },
21089
21090     adjustSize : function(w, h){
21091         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21092         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21093         return s;
21094     },
21095
21096     adjustWidth : function(tag, w){
21097         tag = tag.toLowerCase();
21098         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21099             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21100                 if(tag == 'input'){
21101                     return w + 2;
21102                 }
21103                 if(tag == 'textarea'){
21104                     return w-2;
21105                 }
21106             }else if(Roo.isOpera){
21107                 if(tag == 'input'){
21108                     return w + 2;
21109                 }
21110                 if(tag == 'textarea'){
21111                     return w-2;
21112                 }
21113             }
21114         }
21115         return w;
21116     }
21117 });
21118
21119
21120 // anything other than normal should be considered experimental
21121 Roo.form.Field.msgFx = {
21122     normal : {
21123         show: function(msgEl, f){
21124             msgEl.setDisplayed('block');
21125         },
21126
21127         hide : function(msgEl, f){
21128             msgEl.setDisplayed(false).update('');
21129         }
21130     },
21131
21132     slide : {
21133         show: function(msgEl, f){
21134             msgEl.slideIn('t', {stopFx:true});
21135         },
21136
21137         hide : function(msgEl, f){
21138             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21139         }
21140     },
21141
21142     slideRight : {
21143         show: function(msgEl, f){
21144             msgEl.fixDisplay();
21145             msgEl.alignTo(f.el, 'tl-tr');
21146             msgEl.slideIn('l', {stopFx:true});
21147         },
21148
21149         hide : function(msgEl, f){
21150             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21151         }
21152     }
21153 };/*
21154  * Based on:
21155  * Ext JS Library 1.1.1
21156  * Copyright(c) 2006-2007, Ext JS, LLC.
21157  *
21158  * Originally Released Under LGPL - original licence link has changed is not relivant.
21159  *
21160  * Fork - LGPL
21161  * <script type="text/javascript">
21162  */
21163  
21164
21165 /**
21166  * @class Roo.form.TextField
21167  * @extends Roo.form.Field
21168  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21169  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21170  * @constructor
21171  * Creates a new TextField
21172  * @param {Object} config Configuration options
21173  */
21174 Roo.form.TextField = function(config){
21175     Roo.form.TextField.superclass.constructor.call(this, config);
21176     this.addEvents({
21177         /**
21178          * @event autosize
21179          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21180          * according to the default logic, but this event provides a hook for the developer to apply additional
21181          * logic at runtime to resize the field if needed.
21182              * @param {Roo.form.Field} this This text field
21183              * @param {Number} width The new field width
21184              */
21185         autosize : true
21186     });
21187 };
21188
21189 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21190     /**
21191      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21192      */
21193     grow : false,
21194     /**
21195      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21196      */
21197     growMin : 30,
21198     /**
21199      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21200      */
21201     growMax : 800,
21202     /**
21203      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21204      */
21205     vtype : null,
21206     /**
21207      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21208      */
21209     maskRe : null,
21210     /**
21211      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21212      */
21213     disableKeyFilter : false,
21214     /**
21215      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21216      */
21217     allowBlank : true,
21218     /**
21219      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21220      */
21221     minLength : 0,
21222     /**
21223      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21224      */
21225     maxLength : Number.MAX_VALUE,
21226     /**
21227      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21228      */
21229     minLengthText : "The minimum length for this field is {0}",
21230     /**
21231      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21232      */
21233     maxLengthText : "The maximum length for this field is {0}",
21234     /**
21235      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21236      */
21237     selectOnFocus : false,
21238     /**
21239      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21240      */
21241     blankText : "This field is required",
21242     /**
21243      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21244      * If available, this function will be called only after the basic validators all return true, and will be passed the
21245      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21246      */
21247     validator : null,
21248     /**
21249      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21250      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21251      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21252      */
21253     regex : null,
21254     /**
21255      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21256      */
21257     regexText : "",
21258     /**
21259      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21260      */
21261     emptyText : null,
21262    
21263
21264     // private
21265     initEvents : function()
21266     {
21267         if (this.emptyText) {
21268             this.el.attr('placeholder', this.emptyText);
21269         }
21270         
21271         Roo.form.TextField.superclass.initEvents.call(this);
21272         if(this.validationEvent == 'keyup'){
21273             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21274             this.el.on('keyup', this.filterValidation, this);
21275         }
21276         else if(this.validationEvent !== false){
21277             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21278         }
21279         
21280         if(this.selectOnFocus){
21281             this.on("focus", this.preFocus, this);
21282             
21283         }
21284         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21285             this.el.on("keypress", this.filterKeys, this);
21286         }
21287         if(this.grow){
21288             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21289             this.el.on("click", this.autoSize,  this);
21290         }
21291         if(this.el.is('input[type=password]') && Roo.isSafari){
21292             this.el.on('keydown', this.SafariOnKeyDown, this);
21293         }
21294     },
21295
21296     processValue : function(value){
21297         if(this.stripCharsRe){
21298             var newValue = value.replace(this.stripCharsRe, '');
21299             if(newValue !== value){
21300                 this.setRawValue(newValue);
21301                 return newValue;
21302             }
21303         }
21304         return value;
21305     },
21306
21307     filterValidation : function(e){
21308         if(!e.isNavKeyPress()){
21309             this.validationTask.delay(this.validationDelay);
21310         }
21311     },
21312
21313     // private
21314     onKeyUp : function(e){
21315         if(!e.isNavKeyPress()){
21316             this.autoSize();
21317         }
21318     },
21319
21320     /**
21321      * Resets the current field value to the originally-loaded value and clears any validation messages.
21322      *  
21323      */
21324     reset : function(){
21325         Roo.form.TextField.superclass.reset.call(this);
21326        
21327     },
21328
21329     
21330     // private
21331     preFocus : function(){
21332         
21333         if(this.selectOnFocus){
21334             this.el.dom.select();
21335         }
21336     },
21337
21338     
21339     // private
21340     filterKeys : function(e){
21341         var k = e.getKey();
21342         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21343             return;
21344         }
21345         var c = e.getCharCode(), cc = String.fromCharCode(c);
21346         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21347             return;
21348         }
21349         if(!this.maskRe.test(cc)){
21350             e.stopEvent();
21351         }
21352     },
21353
21354     setValue : function(v){
21355         
21356         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21357         
21358         this.autoSize();
21359     },
21360
21361     /**
21362      * Validates a value according to the field's validation rules and marks the field as invalid
21363      * if the validation fails
21364      * @param {Mixed} value The value to validate
21365      * @return {Boolean} True if the value is valid, else false
21366      */
21367     validateValue : function(value){
21368         if(value.length < 1)  { // if it's blank
21369              if(this.allowBlank){
21370                 this.clearInvalid();
21371                 return true;
21372              }else{
21373                 this.markInvalid(this.blankText);
21374                 return false;
21375              }
21376         }
21377         if(value.length < this.minLength){
21378             this.markInvalid(String.format(this.minLengthText, this.minLength));
21379             return false;
21380         }
21381         if(value.length > this.maxLength){
21382             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21383             return false;
21384         }
21385         if(this.vtype){
21386             var vt = Roo.form.VTypes;
21387             if(!vt[this.vtype](value, this)){
21388                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21389                 return false;
21390             }
21391         }
21392         if(typeof this.validator == "function"){
21393             var msg = this.validator(value);
21394             if(msg !== true){
21395                 this.markInvalid(msg);
21396                 return false;
21397             }
21398         }
21399         if(this.regex && !this.regex.test(value)){
21400             this.markInvalid(this.regexText);
21401             return false;
21402         }
21403         return true;
21404     },
21405
21406     /**
21407      * Selects text in this field
21408      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21409      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21410      */
21411     selectText : function(start, end){
21412         var v = this.getRawValue();
21413         if(v.length > 0){
21414             start = start === undefined ? 0 : start;
21415             end = end === undefined ? v.length : end;
21416             var d = this.el.dom;
21417             if(d.setSelectionRange){
21418                 d.setSelectionRange(start, end);
21419             }else if(d.createTextRange){
21420                 var range = d.createTextRange();
21421                 range.moveStart("character", start);
21422                 range.moveEnd("character", v.length-end);
21423                 range.select();
21424             }
21425         }
21426     },
21427
21428     /**
21429      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21430      * This only takes effect if grow = true, and fires the autosize event.
21431      */
21432     autoSize : function(){
21433         if(!this.grow || !this.rendered){
21434             return;
21435         }
21436         if(!this.metrics){
21437             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21438         }
21439         var el = this.el;
21440         var v = el.dom.value;
21441         var d = document.createElement('div');
21442         d.appendChild(document.createTextNode(v));
21443         v = d.innerHTML;
21444         d = null;
21445         v += "&#160;";
21446         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21447         this.el.setWidth(w);
21448         this.fireEvent("autosize", this, w);
21449     },
21450     
21451     // private
21452     SafariOnKeyDown : function(event)
21453     {
21454         // this is a workaround for a password hang bug on chrome/ webkit.
21455         
21456         var isSelectAll = false;
21457         
21458         if(this.el.dom.selectionEnd > 0){
21459             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21460         }
21461         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21462             event.preventDefault();
21463             this.setValue('');
21464             return;
21465         }
21466         
21467         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21468             
21469             event.preventDefault();
21470             // this is very hacky as keydown always get's upper case.
21471             
21472             var cc = String.fromCharCode(event.getCharCode());
21473             
21474             
21475             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21476             
21477         }
21478         
21479         
21480     }
21481 });/*
21482  * Based on:
21483  * Ext JS Library 1.1.1
21484  * Copyright(c) 2006-2007, Ext JS, LLC.
21485  *
21486  * Originally Released Under LGPL - original licence link has changed is not relivant.
21487  *
21488  * Fork - LGPL
21489  * <script type="text/javascript">
21490  */
21491  
21492 /**
21493  * @class Roo.form.Hidden
21494  * @extends Roo.form.TextField
21495  * Simple Hidden element used on forms 
21496  * 
21497  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21498  * 
21499  * @constructor
21500  * Creates a new Hidden form element.
21501  * @param {Object} config Configuration options
21502  */
21503
21504
21505
21506 // easy hidden field...
21507 Roo.form.Hidden = function(config){
21508     Roo.form.Hidden.superclass.constructor.call(this, config);
21509 };
21510   
21511 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21512     fieldLabel:      '',
21513     inputType:      'hidden',
21514     width:          50,
21515     allowBlank:     true,
21516     labelSeparator: '',
21517     hidden:         true,
21518     itemCls :       'x-form-item-display-none'
21519
21520
21521 });
21522
21523
21524 /*
21525  * Based on:
21526  * Ext JS Library 1.1.1
21527  * Copyright(c) 2006-2007, Ext JS, LLC.
21528  *
21529  * Originally Released Under LGPL - original licence link has changed is not relivant.
21530  *
21531  * Fork - LGPL
21532  * <script type="text/javascript">
21533  */
21534  
21535 /**
21536  * @class Roo.form.TriggerField
21537  * @extends Roo.form.TextField
21538  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21539  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21540  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21541  * for which you can provide a custom implementation.  For example:
21542  * <pre><code>
21543 var trigger = new Roo.form.TriggerField();
21544 trigger.onTriggerClick = myTriggerFn;
21545 trigger.applyTo('my-field');
21546 </code></pre>
21547  *
21548  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21549  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21550  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21551  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21552  * @constructor
21553  * Create a new TriggerField.
21554  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21555  * to the base TextField)
21556  */
21557 Roo.form.TriggerField = function(config){
21558     this.mimicing = false;
21559     Roo.form.TriggerField.superclass.constructor.call(this, config);
21560 };
21561
21562 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21563     /**
21564      * @cfg {String} triggerClass A CSS class to apply to the trigger
21565      */
21566     /**
21567      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21568      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21569      */
21570     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21571     /**
21572      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21573      */
21574     hideTrigger:false,
21575
21576     /** @cfg {Boolean} grow @hide */
21577     /** @cfg {Number} growMin @hide */
21578     /** @cfg {Number} growMax @hide */
21579
21580     /**
21581      * @hide 
21582      * @method
21583      */
21584     autoSize: Roo.emptyFn,
21585     // private
21586     monitorTab : true,
21587     // private
21588     deferHeight : true,
21589
21590     
21591     actionMode : 'wrap',
21592     // private
21593     onResize : function(w, h){
21594         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21595         if(typeof w == 'number'){
21596             var x = w - this.trigger.getWidth();
21597             this.el.setWidth(this.adjustWidth('input', x));
21598             this.trigger.setStyle('left', x+'px');
21599         }
21600     },
21601
21602     // private
21603     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21604
21605     // private
21606     getResizeEl : function(){
21607         return this.wrap;
21608     },
21609
21610     // private
21611     getPositionEl : function(){
21612         return this.wrap;
21613     },
21614
21615     // private
21616     alignErrorIcon : function(){
21617         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21618     },
21619
21620     // private
21621     onRender : function(ct, position){
21622         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21623         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21624         this.trigger = this.wrap.createChild(this.triggerConfig ||
21625                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21626         if(this.hideTrigger){
21627             this.trigger.setDisplayed(false);
21628         }
21629         this.initTrigger();
21630         if(!this.width){
21631             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21632         }
21633     },
21634
21635     // private
21636     initTrigger : function(){
21637         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21638         this.trigger.addClassOnOver('x-form-trigger-over');
21639         this.trigger.addClassOnClick('x-form-trigger-click');
21640     },
21641
21642     // private
21643     onDestroy : function(){
21644         if(this.trigger){
21645             this.trigger.removeAllListeners();
21646             this.trigger.remove();
21647         }
21648         if(this.wrap){
21649             this.wrap.remove();
21650         }
21651         Roo.form.TriggerField.superclass.onDestroy.call(this);
21652     },
21653
21654     // private
21655     onFocus : function(){
21656         Roo.form.TriggerField.superclass.onFocus.call(this);
21657         if(!this.mimicing){
21658             this.wrap.addClass('x-trigger-wrap-focus');
21659             this.mimicing = true;
21660             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21661             if(this.monitorTab){
21662                 this.el.on("keydown", this.checkTab, this);
21663             }
21664         }
21665     },
21666
21667     // private
21668     checkTab : function(e){
21669         if(e.getKey() == e.TAB){
21670             this.triggerBlur();
21671         }
21672     },
21673
21674     // private
21675     onBlur : function(){
21676         // do nothing
21677     },
21678
21679     // private
21680     mimicBlur : function(e, t){
21681         if(!this.wrap.contains(t) && this.validateBlur()){
21682             this.triggerBlur();
21683         }
21684     },
21685
21686     // private
21687     triggerBlur : function(){
21688         this.mimicing = false;
21689         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21690         if(this.monitorTab){
21691             this.el.un("keydown", this.checkTab, this);
21692         }
21693         this.wrap.removeClass('x-trigger-wrap-focus');
21694         Roo.form.TriggerField.superclass.onBlur.call(this);
21695     },
21696
21697     // private
21698     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21699     validateBlur : function(e, t){
21700         return true;
21701     },
21702
21703     // private
21704     onDisable : function(){
21705         Roo.form.TriggerField.superclass.onDisable.call(this);
21706         if(this.wrap){
21707             this.wrap.addClass('x-item-disabled');
21708         }
21709     },
21710
21711     // private
21712     onEnable : function(){
21713         Roo.form.TriggerField.superclass.onEnable.call(this);
21714         if(this.wrap){
21715             this.wrap.removeClass('x-item-disabled');
21716         }
21717     },
21718
21719     // private
21720     onShow : function(){
21721         var ae = this.getActionEl();
21722         
21723         if(ae){
21724             ae.dom.style.display = '';
21725             ae.dom.style.visibility = 'visible';
21726         }
21727     },
21728
21729     // private
21730     
21731     onHide : function(){
21732         var ae = this.getActionEl();
21733         ae.dom.style.display = 'none';
21734     },
21735
21736     /**
21737      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21738      * by an implementing function.
21739      * @method
21740      * @param {EventObject} e
21741      */
21742     onTriggerClick : Roo.emptyFn
21743 });
21744
21745 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21746 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21747 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21748 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21749     initComponent : function(){
21750         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21751
21752         this.triggerConfig = {
21753             tag:'span', cls:'x-form-twin-triggers', cn:[
21754             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21755             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21756         ]};
21757     },
21758
21759     getTrigger : function(index){
21760         return this.triggers[index];
21761     },
21762
21763     initTrigger : function(){
21764         var ts = this.trigger.select('.x-form-trigger', true);
21765         this.wrap.setStyle('overflow', 'hidden');
21766         var triggerField = this;
21767         ts.each(function(t, all, index){
21768             t.hide = function(){
21769                 var w = triggerField.wrap.getWidth();
21770                 this.dom.style.display = 'none';
21771                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21772             };
21773             t.show = function(){
21774                 var w = triggerField.wrap.getWidth();
21775                 this.dom.style.display = '';
21776                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21777             };
21778             var triggerIndex = 'Trigger'+(index+1);
21779
21780             if(this['hide'+triggerIndex]){
21781                 t.dom.style.display = 'none';
21782             }
21783             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21784             t.addClassOnOver('x-form-trigger-over');
21785             t.addClassOnClick('x-form-trigger-click');
21786         }, this);
21787         this.triggers = ts.elements;
21788     },
21789
21790     onTrigger1Click : Roo.emptyFn,
21791     onTrigger2Click : Roo.emptyFn
21792 });/*
21793  * Based on:
21794  * Ext JS Library 1.1.1
21795  * Copyright(c) 2006-2007, Ext JS, LLC.
21796  *
21797  * Originally Released Under LGPL - original licence link has changed is not relivant.
21798  *
21799  * Fork - LGPL
21800  * <script type="text/javascript">
21801  */
21802  
21803 /**
21804  * @class Roo.form.TextArea
21805  * @extends Roo.form.TextField
21806  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21807  * support for auto-sizing.
21808  * @constructor
21809  * Creates a new TextArea
21810  * @param {Object} config Configuration options
21811  */
21812 Roo.form.TextArea = function(config){
21813     Roo.form.TextArea.superclass.constructor.call(this, config);
21814     // these are provided exchanges for backwards compat
21815     // minHeight/maxHeight were replaced by growMin/growMax to be
21816     // compatible with TextField growing config values
21817     if(this.minHeight !== undefined){
21818         this.growMin = this.minHeight;
21819     }
21820     if(this.maxHeight !== undefined){
21821         this.growMax = this.maxHeight;
21822     }
21823 };
21824
21825 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21826     /**
21827      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21828      */
21829     growMin : 60,
21830     /**
21831      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21832      */
21833     growMax: 1000,
21834     /**
21835      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21836      * in the field (equivalent to setting overflow: hidden, defaults to false)
21837      */
21838     preventScrollbars: false,
21839     /**
21840      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21841      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21842      */
21843
21844     // private
21845     onRender : function(ct, position){
21846         if(!this.el){
21847             this.defaultAutoCreate = {
21848                 tag: "textarea",
21849                 style:"width:300px;height:60px;",
21850                 autocomplete: "new-password"
21851             };
21852         }
21853         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21854         if(this.grow){
21855             this.textSizeEl = Roo.DomHelper.append(document.body, {
21856                 tag: "pre", cls: "x-form-grow-sizer"
21857             });
21858             if(this.preventScrollbars){
21859                 this.el.setStyle("overflow", "hidden");
21860             }
21861             this.el.setHeight(this.growMin);
21862         }
21863     },
21864
21865     onDestroy : function(){
21866         if(this.textSizeEl){
21867             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21868         }
21869         Roo.form.TextArea.superclass.onDestroy.call(this);
21870     },
21871
21872     // private
21873     onKeyUp : function(e){
21874         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21875             this.autoSize();
21876         }
21877     },
21878
21879     /**
21880      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21881      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21882      */
21883     autoSize : function(){
21884         if(!this.grow || !this.textSizeEl){
21885             return;
21886         }
21887         var el = this.el;
21888         var v = el.dom.value;
21889         var ts = this.textSizeEl;
21890
21891         ts.innerHTML = '';
21892         ts.appendChild(document.createTextNode(v));
21893         v = ts.innerHTML;
21894
21895         Roo.fly(ts).setWidth(this.el.getWidth());
21896         if(v.length < 1){
21897             v = "&#160;&#160;";
21898         }else{
21899             if(Roo.isIE){
21900                 v = v.replace(/\n/g, '<p>&#160;</p>');
21901             }
21902             v += "&#160;\n&#160;";
21903         }
21904         ts.innerHTML = v;
21905         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21906         if(h != this.lastHeight){
21907             this.lastHeight = h;
21908             this.el.setHeight(h);
21909             this.fireEvent("autosize", this, h);
21910         }
21911     }
21912 });/*
21913  * Based on:
21914  * Ext JS Library 1.1.1
21915  * Copyright(c) 2006-2007, Ext JS, LLC.
21916  *
21917  * Originally Released Under LGPL - original licence link has changed is not relivant.
21918  *
21919  * Fork - LGPL
21920  * <script type="text/javascript">
21921  */
21922  
21923
21924 /**
21925  * @class Roo.form.NumberField
21926  * @extends Roo.form.TextField
21927  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21928  * @constructor
21929  * Creates a new NumberField
21930  * @param {Object} config Configuration options
21931  */
21932 Roo.form.NumberField = function(config){
21933     Roo.form.NumberField.superclass.constructor.call(this, config);
21934 };
21935
21936 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21937     /**
21938      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21939      */
21940     fieldClass: "x-form-field x-form-num-field",
21941     /**
21942      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21943      */
21944     allowDecimals : true,
21945     /**
21946      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21947      */
21948     decimalSeparator : ".",
21949     /**
21950      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21951      */
21952     decimalPrecision : 2,
21953     /**
21954      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21955      */
21956     allowNegative : true,
21957     /**
21958      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21959      */
21960     minValue : Number.NEGATIVE_INFINITY,
21961     /**
21962      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21963      */
21964     maxValue : Number.MAX_VALUE,
21965     /**
21966      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21967      */
21968     minText : "The minimum value for this field is {0}",
21969     /**
21970      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21971      */
21972     maxText : "The maximum value for this field is {0}",
21973     /**
21974      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21975      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21976      */
21977     nanText : "{0} is not a valid number",
21978
21979     // private
21980     initEvents : function(){
21981         Roo.form.NumberField.superclass.initEvents.call(this);
21982         var allowed = "0123456789";
21983         if(this.allowDecimals){
21984             allowed += this.decimalSeparator;
21985         }
21986         if(this.allowNegative){
21987             allowed += "-";
21988         }
21989         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21990         var keyPress = function(e){
21991             var k = e.getKey();
21992             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21993                 return;
21994             }
21995             var c = e.getCharCode();
21996             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21997                 e.stopEvent();
21998             }
21999         };
22000         this.el.on("keypress", keyPress, this);
22001     },
22002
22003     // private
22004     validateValue : function(value){
22005         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22006             return false;
22007         }
22008         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22009              return true;
22010         }
22011         var num = this.parseValue(value);
22012         if(isNaN(num)){
22013             this.markInvalid(String.format(this.nanText, value));
22014             return false;
22015         }
22016         if(num < this.minValue){
22017             this.markInvalid(String.format(this.minText, this.minValue));
22018             return false;
22019         }
22020         if(num > this.maxValue){
22021             this.markInvalid(String.format(this.maxText, this.maxValue));
22022             return false;
22023         }
22024         return true;
22025     },
22026
22027     getValue : function(){
22028         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22029     },
22030
22031     // private
22032     parseValue : function(value){
22033         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22034         return isNaN(value) ? '' : value;
22035     },
22036
22037     // private
22038     fixPrecision : function(value){
22039         var nan = isNaN(value);
22040         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22041             return nan ? '' : value;
22042         }
22043         return parseFloat(value).toFixed(this.decimalPrecision);
22044     },
22045
22046     setValue : function(v){
22047         v = this.fixPrecision(v);
22048         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22049     },
22050
22051     // private
22052     decimalPrecisionFcn : function(v){
22053         return Math.floor(v);
22054     },
22055
22056     beforeBlur : function(){
22057         var v = this.parseValue(this.getRawValue());
22058         if(v){
22059             this.setValue(v);
22060         }
22061     }
22062 });/*
22063  * Based on:
22064  * Ext JS Library 1.1.1
22065  * Copyright(c) 2006-2007, Ext JS, LLC.
22066  *
22067  * Originally Released Under LGPL - original licence link has changed is not relivant.
22068  *
22069  * Fork - LGPL
22070  * <script type="text/javascript">
22071  */
22072  
22073 /**
22074  * @class Roo.form.DateField
22075  * @extends Roo.form.TriggerField
22076  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22077 * @constructor
22078 * Create a new DateField
22079 * @param {Object} config
22080  */
22081 Roo.form.DateField = function(config){
22082     Roo.form.DateField.superclass.constructor.call(this, config);
22083     
22084       this.addEvents({
22085          
22086         /**
22087          * @event select
22088          * Fires when a date is selected
22089              * @param {Roo.form.DateField} combo This combo box
22090              * @param {Date} date The date selected
22091              */
22092         'select' : true
22093          
22094     });
22095     
22096     
22097     if(typeof this.minValue == "string") {
22098         this.minValue = this.parseDate(this.minValue);
22099     }
22100     if(typeof this.maxValue == "string") {
22101         this.maxValue = this.parseDate(this.maxValue);
22102     }
22103     this.ddMatch = null;
22104     if(this.disabledDates){
22105         var dd = this.disabledDates;
22106         var re = "(?:";
22107         for(var i = 0; i < dd.length; i++){
22108             re += dd[i];
22109             if(i != dd.length-1) {
22110                 re += "|";
22111             }
22112         }
22113         this.ddMatch = new RegExp(re + ")");
22114     }
22115 };
22116
22117 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22118     /**
22119      * @cfg {String} format
22120      * The default date format string which can be overriden for localization support.  The format must be
22121      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22122      */
22123     format : "m/d/y",
22124     /**
22125      * @cfg {String} altFormats
22126      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22127      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22128      */
22129     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22130     /**
22131      * @cfg {Array} disabledDays
22132      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22133      */
22134     disabledDays : null,
22135     /**
22136      * @cfg {String} disabledDaysText
22137      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22138      */
22139     disabledDaysText : "Disabled",
22140     /**
22141      * @cfg {Array} disabledDates
22142      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22143      * expression so they are very powerful. Some examples:
22144      * <ul>
22145      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22146      * <li>["03/08", "09/16"] would disable those days for every year</li>
22147      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22148      * <li>["03/../2006"] would disable every day in March 2006</li>
22149      * <li>["^03"] would disable every day in every March</li>
22150      * </ul>
22151      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22152      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22153      */
22154     disabledDates : null,
22155     /**
22156      * @cfg {String} disabledDatesText
22157      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22158      */
22159     disabledDatesText : "Disabled",
22160     /**
22161      * @cfg {Date/String} minValue
22162      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22163      * valid format (defaults to null).
22164      */
22165     minValue : null,
22166     /**
22167      * @cfg {Date/String} maxValue
22168      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22169      * valid format (defaults to null).
22170      */
22171     maxValue : null,
22172     /**
22173      * @cfg {String} minText
22174      * The error text to display when the date in the cell is before minValue (defaults to
22175      * 'The date in this field must be after {minValue}').
22176      */
22177     minText : "The date in this field must be equal to or after {0}",
22178     /**
22179      * @cfg {String} maxText
22180      * The error text to display when the date in the cell is after maxValue (defaults to
22181      * 'The date in this field must be before {maxValue}').
22182      */
22183     maxText : "The date in this field must be equal to or before {0}",
22184     /**
22185      * @cfg {String} invalidText
22186      * The error text to display when the date in the field is invalid (defaults to
22187      * '{value} is not a valid date - it must be in the format {format}').
22188      */
22189     invalidText : "{0} is not a valid date - it must be in the format {1}",
22190     /**
22191      * @cfg {String} triggerClass
22192      * An additional CSS class used to style the trigger button.  The trigger will always get the
22193      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22194      * which displays a calendar icon).
22195      */
22196     triggerClass : 'x-form-date-trigger',
22197     
22198
22199     /**
22200      * @cfg {Boolean} useIso
22201      * if enabled, then the date field will use a hidden field to store the 
22202      * real value as iso formated date. default (false)
22203      */ 
22204     useIso : false,
22205     /**
22206      * @cfg {String/Object} autoCreate
22207      * A DomHelper element spec, or true for a default element spec (defaults to
22208      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22209      */ 
22210     // private
22211     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22212     
22213     // private
22214     hiddenField: false,
22215     
22216     onRender : function(ct, position)
22217     {
22218         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22219         if (this.useIso) {
22220             //this.el.dom.removeAttribute('name'); 
22221             Roo.log("Changing name?");
22222             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22223             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22224                     'before', true);
22225             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22226             // prevent input submission
22227             this.hiddenName = this.name;
22228         }
22229             
22230             
22231     },
22232     
22233     // private
22234     validateValue : function(value)
22235     {
22236         value = this.formatDate(value);
22237         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22238             Roo.log('super failed');
22239             return false;
22240         }
22241         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22242              return true;
22243         }
22244         var svalue = value;
22245         value = this.parseDate(value);
22246         if(!value){
22247             Roo.log('parse date failed' + svalue);
22248             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22249             return false;
22250         }
22251         var time = value.getTime();
22252         if(this.minValue && time < this.minValue.getTime()){
22253             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22254             return false;
22255         }
22256         if(this.maxValue && time > this.maxValue.getTime()){
22257             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22258             return false;
22259         }
22260         if(this.disabledDays){
22261             var day = value.getDay();
22262             for(var i = 0; i < this.disabledDays.length; i++) {
22263                 if(day === this.disabledDays[i]){
22264                     this.markInvalid(this.disabledDaysText);
22265                     return false;
22266                 }
22267             }
22268         }
22269         var fvalue = this.formatDate(value);
22270         if(this.ddMatch && this.ddMatch.test(fvalue)){
22271             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22272             return false;
22273         }
22274         return true;
22275     },
22276
22277     // private
22278     // Provides logic to override the default TriggerField.validateBlur which just returns true
22279     validateBlur : function(){
22280         return !this.menu || !this.menu.isVisible();
22281     },
22282     
22283     getName: function()
22284     {
22285         // returns hidden if it's set..
22286         if (!this.rendered) {return ''};
22287         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22288         
22289     },
22290
22291     /**
22292      * Returns the current date value of the date field.
22293      * @return {Date} The date value
22294      */
22295     getValue : function(){
22296         
22297         return  this.hiddenField ?
22298                 this.hiddenField.value :
22299                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22300     },
22301
22302     /**
22303      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22304      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22305      * (the default format used is "m/d/y").
22306      * <br />Usage:
22307      * <pre><code>
22308 //All of these calls set the same date value (May 4, 2006)
22309
22310 //Pass a date object:
22311 var dt = new Date('5/4/06');
22312 dateField.setValue(dt);
22313
22314 //Pass a date string (default format):
22315 dateField.setValue('5/4/06');
22316
22317 //Pass a date string (custom format):
22318 dateField.format = 'Y-m-d';
22319 dateField.setValue('2006-5-4');
22320 </code></pre>
22321      * @param {String/Date} date The date or valid date string
22322      */
22323     setValue : function(date){
22324         if (this.hiddenField) {
22325             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22326         }
22327         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22328         // make sure the value field is always stored as a date..
22329         this.value = this.parseDate(date);
22330         
22331         
22332     },
22333
22334     // private
22335     parseDate : function(value){
22336         if(!value || value instanceof Date){
22337             return value;
22338         }
22339         var v = Date.parseDate(value, this.format);
22340          if (!v && this.useIso) {
22341             v = Date.parseDate(value, 'Y-m-d');
22342         }
22343         if(!v && this.altFormats){
22344             if(!this.altFormatsArray){
22345                 this.altFormatsArray = this.altFormats.split("|");
22346             }
22347             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22348                 v = Date.parseDate(value, this.altFormatsArray[i]);
22349             }
22350         }
22351         return v;
22352     },
22353
22354     // private
22355     formatDate : function(date, fmt){
22356         return (!date || !(date instanceof Date)) ?
22357                date : date.dateFormat(fmt || this.format);
22358     },
22359
22360     // private
22361     menuListeners : {
22362         select: function(m, d){
22363             
22364             this.setValue(d);
22365             this.fireEvent('select', this, d);
22366         },
22367         show : function(){ // retain focus styling
22368             this.onFocus();
22369         },
22370         hide : function(){
22371             this.focus.defer(10, this);
22372             var ml = this.menuListeners;
22373             this.menu.un("select", ml.select,  this);
22374             this.menu.un("show", ml.show,  this);
22375             this.menu.un("hide", ml.hide,  this);
22376         }
22377     },
22378
22379     // private
22380     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22381     onTriggerClick : function(){
22382         if(this.disabled){
22383             return;
22384         }
22385         if(this.menu == null){
22386             this.menu = new Roo.menu.DateMenu();
22387         }
22388         Roo.apply(this.menu.picker,  {
22389             showClear: this.allowBlank,
22390             minDate : this.minValue,
22391             maxDate : this.maxValue,
22392             disabledDatesRE : this.ddMatch,
22393             disabledDatesText : this.disabledDatesText,
22394             disabledDays : this.disabledDays,
22395             disabledDaysText : this.disabledDaysText,
22396             format : this.useIso ? 'Y-m-d' : this.format,
22397             minText : String.format(this.minText, this.formatDate(this.minValue)),
22398             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22399         });
22400         this.menu.on(Roo.apply({}, this.menuListeners, {
22401             scope:this
22402         }));
22403         this.menu.picker.setValue(this.getValue() || new Date());
22404         this.menu.show(this.el, "tl-bl?");
22405     },
22406
22407     beforeBlur : function(){
22408         var v = this.parseDate(this.getRawValue());
22409         if(v){
22410             this.setValue(v);
22411         }
22412     },
22413
22414     /*@
22415      * overide
22416      * 
22417      */
22418     isDirty : function() {
22419         if(this.disabled) {
22420             return false;
22421         }
22422         
22423         if(typeof(this.startValue) === 'undefined'){
22424             return false;
22425         }
22426         
22427         return String(this.getValue()) !== String(this.startValue);
22428         
22429     }
22430 });/*
22431  * Based on:
22432  * Ext JS Library 1.1.1
22433  * Copyright(c) 2006-2007, Ext JS, LLC.
22434  *
22435  * Originally Released Under LGPL - original licence link has changed is not relivant.
22436  *
22437  * Fork - LGPL
22438  * <script type="text/javascript">
22439  */
22440  
22441 /**
22442  * @class Roo.form.MonthField
22443  * @extends Roo.form.TriggerField
22444  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22445 * @constructor
22446 * Create a new MonthField
22447 * @param {Object} config
22448  */
22449 Roo.form.MonthField = function(config){
22450     
22451     Roo.form.MonthField.superclass.constructor.call(this, config);
22452     
22453       this.addEvents({
22454          
22455         /**
22456          * @event select
22457          * Fires when a date is selected
22458              * @param {Roo.form.MonthFieeld} combo This combo box
22459              * @param {Date} date The date selected
22460              */
22461         'select' : true
22462          
22463     });
22464     
22465     
22466     if(typeof this.minValue == "string") {
22467         this.minValue = this.parseDate(this.minValue);
22468     }
22469     if(typeof this.maxValue == "string") {
22470         this.maxValue = this.parseDate(this.maxValue);
22471     }
22472     this.ddMatch = null;
22473     if(this.disabledDates){
22474         var dd = this.disabledDates;
22475         var re = "(?:";
22476         for(var i = 0; i < dd.length; i++){
22477             re += dd[i];
22478             if(i != dd.length-1) {
22479                 re += "|";
22480             }
22481         }
22482         this.ddMatch = new RegExp(re + ")");
22483     }
22484 };
22485
22486 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22487     /**
22488      * @cfg {String} format
22489      * The default date format string which can be overriden for localization support.  The format must be
22490      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22491      */
22492     format : "M Y",
22493     /**
22494      * @cfg {String} altFormats
22495      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22496      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22497      */
22498     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22499     /**
22500      * @cfg {Array} disabledDays
22501      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22502      */
22503     disabledDays : [0,1,2,3,4,5,6],
22504     /**
22505      * @cfg {String} disabledDaysText
22506      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22507      */
22508     disabledDaysText : "Disabled",
22509     /**
22510      * @cfg {Array} disabledDates
22511      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22512      * expression so they are very powerful. Some examples:
22513      * <ul>
22514      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22515      * <li>["03/08", "09/16"] would disable those days for every year</li>
22516      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22517      * <li>["03/../2006"] would disable every day in March 2006</li>
22518      * <li>["^03"] would disable every day in every March</li>
22519      * </ul>
22520      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22521      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22522      */
22523     disabledDates : null,
22524     /**
22525      * @cfg {String} disabledDatesText
22526      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22527      */
22528     disabledDatesText : "Disabled",
22529     /**
22530      * @cfg {Date/String} minValue
22531      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22532      * valid format (defaults to null).
22533      */
22534     minValue : null,
22535     /**
22536      * @cfg {Date/String} maxValue
22537      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22538      * valid format (defaults to null).
22539      */
22540     maxValue : null,
22541     /**
22542      * @cfg {String} minText
22543      * The error text to display when the date in the cell is before minValue (defaults to
22544      * 'The date in this field must be after {minValue}').
22545      */
22546     minText : "The date in this field must be equal to or after {0}",
22547     /**
22548      * @cfg {String} maxTextf
22549      * The error text to display when the date in the cell is after maxValue (defaults to
22550      * 'The date in this field must be before {maxValue}').
22551      */
22552     maxText : "The date in this field must be equal to or before {0}",
22553     /**
22554      * @cfg {String} invalidText
22555      * The error text to display when the date in the field is invalid (defaults to
22556      * '{value} is not a valid date - it must be in the format {format}').
22557      */
22558     invalidText : "{0} is not a valid date - it must be in the format {1}",
22559     /**
22560      * @cfg {String} triggerClass
22561      * An additional CSS class used to style the trigger button.  The trigger will always get the
22562      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22563      * which displays a calendar icon).
22564      */
22565     triggerClass : 'x-form-date-trigger',
22566     
22567
22568     /**
22569      * @cfg {Boolean} useIso
22570      * if enabled, then the date field will use a hidden field to store the 
22571      * real value as iso formated date. default (true)
22572      */ 
22573     useIso : true,
22574     /**
22575      * @cfg {String/Object} autoCreate
22576      * A DomHelper element spec, or true for a default element spec (defaults to
22577      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22578      */ 
22579     // private
22580     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22581     
22582     // private
22583     hiddenField: false,
22584     
22585     hideMonthPicker : false,
22586     
22587     onRender : function(ct, position)
22588     {
22589         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22590         if (this.useIso) {
22591             this.el.dom.removeAttribute('name'); 
22592             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22593                     'before', true);
22594             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22595             // prevent input submission
22596             this.hiddenName = this.name;
22597         }
22598             
22599             
22600     },
22601     
22602     // private
22603     validateValue : function(value)
22604     {
22605         value = this.formatDate(value);
22606         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22607             return false;
22608         }
22609         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22610              return true;
22611         }
22612         var svalue = value;
22613         value = this.parseDate(value);
22614         if(!value){
22615             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22616             return false;
22617         }
22618         var time = value.getTime();
22619         if(this.minValue && time < this.minValue.getTime()){
22620             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22621             return false;
22622         }
22623         if(this.maxValue && time > this.maxValue.getTime()){
22624             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22625             return false;
22626         }
22627         /*if(this.disabledDays){
22628             var day = value.getDay();
22629             for(var i = 0; i < this.disabledDays.length; i++) {
22630                 if(day === this.disabledDays[i]){
22631                     this.markInvalid(this.disabledDaysText);
22632                     return false;
22633                 }
22634             }
22635         }
22636         */
22637         var fvalue = this.formatDate(value);
22638         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22639             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22640             return false;
22641         }
22642         */
22643         return true;
22644     },
22645
22646     // private
22647     // Provides logic to override the default TriggerField.validateBlur which just returns true
22648     validateBlur : function(){
22649         return !this.menu || !this.menu.isVisible();
22650     },
22651
22652     /**
22653      * Returns the current date value of the date field.
22654      * @return {Date} The date value
22655      */
22656     getValue : function(){
22657         
22658         
22659         
22660         return  this.hiddenField ?
22661                 this.hiddenField.value :
22662                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22663     },
22664
22665     /**
22666      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22667      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22668      * (the default format used is "m/d/y").
22669      * <br />Usage:
22670      * <pre><code>
22671 //All of these calls set the same date value (May 4, 2006)
22672
22673 //Pass a date object:
22674 var dt = new Date('5/4/06');
22675 monthField.setValue(dt);
22676
22677 //Pass a date string (default format):
22678 monthField.setValue('5/4/06');
22679
22680 //Pass a date string (custom format):
22681 monthField.format = 'Y-m-d';
22682 monthField.setValue('2006-5-4');
22683 </code></pre>
22684      * @param {String/Date} date The date or valid date string
22685      */
22686     setValue : function(date){
22687         Roo.log('month setValue' + date);
22688         // can only be first of month..
22689         
22690         var val = this.parseDate(date);
22691         
22692         if (this.hiddenField) {
22693             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22694         }
22695         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22696         this.value = this.parseDate(date);
22697     },
22698
22699     // private
22700     parseDate : function(value){
22701         if(!value || value instanceof Date){
22702             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22703             return value;
22704         }
22705         var v = Date.parseDate(value, this.format);
22706         if (!v && this.useIso) {
22707             v = Date.parseDate(value, 'Y-m-d');
22708         }
22709         if (v) {
22710             // 
22711             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22712         }
22713         
22714         
22715         if(!v && this.altFormats){
22716             if(!this.altFormatsArray){
22717                 this.altFormatsArray = this.altFormats.split("|");
22718             }
22719             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22720                 v = Date.parseDate(value, this.altFormatsArray[i]);
22721             }
22722         }
22723         return v;
22724     },
22725
22726     // private
22727     formatDate : function(date, fmt){
22728         return (!date || !(date instanceof Date)) ?
22729                date : date.dateFormat(fmt || this.format);
22730     },
22731
22732     // private
22733     menuListeners : {
22734         select: function(m, d){
22735             this.setValue(d);
22736             this.fireEvent('select', this, d);
22737         },
22738         show : function(){ // retain focus styling
22739             this.onFocus();
22740         },
22741         hide : function(){
22742             this.focus.defer(10, this);
22743             var ml = this.menuListeners;
22744             this.menu.un("select", ml.select,  this);
22745             this.menu.un("show", ml.show,  this);
22746             this.menu.un("hide", ml.hide,  this);
22747         }
22748     },
22749     // private
22750     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22751     onTriggerClick : function(){
22752         if(this.disabled){
22753             return;
22754         }
22755         if(this.menu == null){
22756             this.menu = new Roo.menu.DateMenu();
22757            
22758         }
22759         
22760         Roo.apply(this.menu.picker,  {
22761             
22762             showClear: this.allowBlank,
22763             minDate : this.minValue,
22764             maxDate : this.maxValue,
22765             disabledDatesRE : this.ddMatch,
22766             disabledDatesText : this.disabledDatesText,
22767             
22768             format : this.useIso ? 'Y-m-d' : this.format,
22769             minText : String.format(this.minText, this.formatDate(this.minValue)),
22770             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22771             
22772         });
22773          this.menu.on(Roo.apply({}, this.menuListeners, {
22774             scope:this
22775         }));
22776        
22777         
22778         var m = this.menu;
22779         var p = m.picker;
22780         
22781         // hide month picker get's called when we called by 'before hide';
22782         
22783         var ignorehide = true;
22784         p.hideMonthPicker  = function(disableAnim){
22785             if (ignorehide) {
22786                 return;
22787             }
22788              if(this.monthPicker){
22789                 Roo.log("hideMonthPicker called");
22790                 if(disableAnim === true){
22791                     this.monthPicker.hide();
22792                 }else{
22793                     this.monthPicker.slideOut('t', {duration:.2});
22794                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22795                     p.fireEvent("select", this, this.value);
22796                     m.hide();
22797                 }
22798             }
22799         }
22800         
22801         Roo.log('picker set value');
22802         Roo.log(this.getValue());
22803         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22804         m.show(this.el, 'tl-bl?');
22805         ignorehide  = false;
22806         // this will trigger hideMonthPicker..
22807         
22808         
22809         // hidden the day picker
22810         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22811         
22812         
22813         
22814       
22815         
22816         p.showMonthPicker.defer(100, p);
22817     
22818         
22819        
22820     },
22821
22822     beforeBlur : function(){
22823         var v = this.parseDate(this.getRawValue());
22824         if(v){
22825             this.setValue(v);
22826         }
22827     }
22828
22829     /** @cfg {Boolean} grow @hide */
22830     /** @cfg {Number} growMin @hide */
22831     /** @cfg {Number} growMax @hide */
22832     /**
22833      * @hide
22834      * @method autoSize
22835      */
22836 });/*
22837  * Based on:
22838  * Ext JS Library 1.1.1
22839  * Copyright(c) 2006-2007, Ext JS, LLC.
22840  *
22841  * Originally Released Under LGPL - original licence link has changed is not relivant.
22842  *
22843  * Fork - LGPL
22844  * <script type="text/javascript">
22845  */
22846  
22847
22848 /**
22849  * @class Roo.form.ComboBox
22850  * @extends Roo.form.TriggerField
22851  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22852  * @constructor
22853  * Create a new ComboBox.
22854  * @param {Object} config Configuration options
22855  */
22856 Roo.form.ComboBox = function(config){
22857     Roo.form.ComboBox.superclass.constructor.call(this, config);
22858     this.addEvents({
22859         /**
22860          * @event expand
22861          * Fires when the dropdown list is expanded
22862              * @param {Roo.form.ComboBox} combo This combo box
22863              */
22864         'expand' : true,
22865         /**
22866          * @event collapse
22867          * Fires when the dropdown list is collapsed
22868              * @param {Roo.form.ComboBox} combo This combo box
22869              */
22870         'collapse' : true,
22871         /**
22872          * @event beforeselect
22873          * Fires before a list item is selected. Return false to cancel the selection.
22874              * @param {Roo.form.ComboBox} combo This combo box
22875              * @param {Roo.data.Record} record The data record returned from the underlying store
22876              * @param {Number} index The index of the selected item in the dropdown list
22877              */
22878         'beforeselect' : true,
22879         /**
22880          * @event select
22881          * Fires when a list item is selected
22882              * @param {Roo.form.ComboBox} combo This combo box
22883              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22884              * @param {Number} index The index of the selected item in the dropdown list
22885              */
22886         'select' : true,
22887         /**
22888          * @event beforequery
22889          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22890          * The event object passed has these properties:
22891              * @param {Roo.form.ComboBox} combo This combo box
22892              * @param {String} query The query
22893              * @param {Boolean} forceAll true to force "all" query
22894              * @param {Boolean} cancel true to cancel the query
22895              * @param {Object} e The query event object
22896              */
22897         'beforequery': true,
22898          /**
22899          * @event add
22900          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22901              * @param {Roo.form.ComboBox} combo This combo box
22902              */
22903         'add' : true,
22904         /**
22905          * @event edit
22906          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22907              * @param {Roo.form.ComboBox} combo This combo box
22908              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22909              */
22910         'edit' : true
22911         
22912         
22913     });
22914     if(this.transform){
22915         this.allowDomMove = false;
22916         var s = Roo.getDom(this.transform);
22917         if(!this.hiddenName){
22918             this.hiddenName = s.name;
22919         }
22920         if(!this.store){
22921             this.mode = 'local';
22922             var d = [], opts = s.options;
22923             for(var i = 0, len = opts.length;i < len; i++){
22924                 var o = opts[i];
22925                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22926                 if(o.selected) {
22927                     this.value = value;
22928                 }
22929                 d.push([value, o.text]);
22930             }
22931             this.store = new Roo.data.SimpleStore({
22932                 'id': 0,
22933                 fields: ['value', 'text'],
22934                 data : d
22935             });
22936             this.valueField = 'value';
22937             this.displayField = 'text';
22938         }
22939         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22940         if(!this.lazyRender){
22941             this.target = true;
22942             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22943             s.parentNode.removeChild(s); // remove it
22944             this.render(this.el.parentNode);
22945         }else{
22946             s.parentNode.removeChild(s); // remove it
22947         }
22948
22949     }
22950     if (this.store) {
22951         this.store = Roo.factory(this.store, Roo.data);
22952     }
22953     
22954     this.selectedIndex = -1;
22955     if(this.mode == 'local'){
22956         if(config.queryDelay === undefined){
22957             this.queryDelay = 10;
22958         }
22959         if(config.minChars === undefined){
22960             this.minChars = 0;
22961         }
22962     }
22963 };
22964
22965 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22966     /**
22967      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22968      */
22969     /**
22970      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22971      * rendering into an Roo.Editor, defaults to false)
22972      */
22973     /**
22974      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22975      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22976      */
22977     /**
22978      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22979      */
22980     /**
22981      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22982      * the dropdown list (defaults to undefined, with no header element)
22983      */
22984
22985      /**
22986      * @cfg {String/Roo.Template} tpl The template to use to render the output
22987      */
22988      
22989     // private
22990     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22991     /**
22992      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22993      */
22994     listWidth: undefined,
22995     /**
22996      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22997      * mode = 'remote' or 'text' if mode = 'local')
22998      */
22999     displayField: undefined,
23000     /**
23001      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23002      * mode = 'remote' or 'value' if mode = 'local'). 
23003      * Note: use of a valueField requires the user make a selection
23004      * in order for a value to be mapped.
23005      */
23006     valueField: undefined,
23007     
23008     
23009     /**
23010      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23011      * field's data value (defaults to the underlying DOM element's name)
23012      */
23013     hiddenName: undefined,
23014     /**
23015      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23016      */
23017     listClass: '',
23018     /**
23019      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23020      */
23021     selectedClass: 'x-combo-selected',
23022     /**
23023      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23024      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23025      * which displays a downward arrow icon).
23026      */
23027     triggerClass : 'x-form-arrow-trigger',
23028     /**
23029      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23030      */
23031     shadow:'sides',
23032     /**
23033      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23034      * anchor positions (defaults to 'tl-bl')
23035      */
23036     listAlign: 'tl-bl?',
23037     /**
23038      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23039      */
23040     maxHeight: 300,
23041     /**
23042      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23043      * query specified by the allQuery config option (defaults to 'query')
23044      */
23045     triggerAction: 'query',
23046     /**
23047      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23048      * (defaults to 4, does not apply if editable = false)
23049      */
23050     minChars : 4,
23051     /**
23052      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23053      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23054      */
23055     typeAhead: false,
23056     /**
23057      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23058      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23059      */
23060     queryDelay: 500,
23061     /**
23062      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23063      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23064      */
23065     pageSize: 0,
23066     /**
23067      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23068      * when editable = true (defaults to false)
23069      */
23070     selectOnFocus:false,
23071     /**
23072      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23073      */
23074     queryParam: 'query',
23075     /**
23076      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23077      * when mode = 'remote' (defaults to 'Loading...')
23078      */
23079     loadingText: 'Loading...',
23080     /**
23081      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23082      */
23083     resizable: false,
23084     /**
23085      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23086      */
23087     handleHeight : 8,
23088     /**
23089      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23090      * traditional select (defaults to true)
23091      */
23092     editable: true,
23093     /**
23094      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23095      */
23096     allQuery: '',
23097     /**
23098      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23099      */
23100     mode: 'remote',
23101     /**
23102      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23103      * listWidth has a higher value)
23104      */
23105     minListWidth : 70,
23106     /**
23107      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23108      * allow the user to set arbitrary text into the field (defaults to false)
23109      */
23110     forceSelection:false,
23111     /**
23112      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23113      * if typeAhead = true (defaults to 250)
23114      */
23115     typeAheadDelay : 250,
23116     /**
23117      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23118      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23119      */
23120     valueNotFoundText : undefined,
23121     /**
23122      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23123      */
23124     blockFocus : false,
23125     
23126     /**
23127      * @cfg {Boolean} disableClear Disable showing of clear button.
23128      */
23129     disableClear : false,
23130     /**
23131      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23132      */
23133     alwaysQuery : false,
23134     
23135     //private
23136     addicon : false,
23137     editicon: false,
23138     
23139     // element that contains real text value.. (when hidden is used..)
23140      
23141     // private
23142     onRender : function(ct, position){
23143         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23144         if(this.hiddenName){
23145             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23146                     'before', true);
23147             this.hiddenField.value =
23148                 this.hiddenValue !== undefined ? this.hiddenValue :
23149                 this.value !== undefined ? this.value : '';
23150
23151             // prevent input submission
23152             this.el.dom.removeAttribute('name');
23153              
23154              
23155         }
23156         if(Roo.isGecko){
23157             this.el.dom.setAttribute('autocomplete', 'off');
23158         }
23159
23160         var cls = 'x-combo-list';
23161
23162         this.list = new Roo.Layer({
23163             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23164         });
23165
23166         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23167         this.list.setWidth(lw);
23168         this.list.swallowEvent('mousewheel');
23169         this.assetHeight = 0;
23170
23171         if(this.title){
23172             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23173             this.assetHeight += this.header.getHeight();
23174         }
23175
23176         this.innerList = this.list.createChild({cls:cls+'-inner'});
23177         this.innerList.on('mouseover', this.onViewOver, this);
23178         this.innerList.on('mousemove', this.onViewMove, this);
23179         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23180         
23181         if(this.allowBlank && !this.pageSize && !this.disableClear){
23182             this.footer = this.list.createChild({cls:cls+'-ft'});
23183             this.pageTb = new Roo.Toolbar(this.footer);
23184            
23185         }
23186         if(this.pageSize){
23187             this.footer = this.list.createChild({cls:cls+'-ft'});
23188             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23189                     {pageSize: this.pageSize});
23190             
23191         }
23192         
23193         if (this.pageTb && this.allowBlank && !this.disableClear) {
23194             var _this = this;
23195             this.pageTb.add(new Roo.Toolbar.Fill(), {
23196                 cls: 'x-btn-icon x-btn-clear',
23197                 text: '&#160;',
23198                 handler: function()
23199                 {
23200                     _this.collapse();
23201                     _this.clearValue();
23202                     _this.onSelect(false, -1);
23203                 }
23204             });
23205         }
23206         if (this.footer) {
23207             this.assetHeight += this.footer.getHeight();
23208         }
23209         
23210
23211         if(!this.tpl){
23212             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23213         }
23214
23215         this.view = new Roo.View(this.innerList, this.tpl, {
23216             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23217         });
23218
23219         this.view.on('click', this.onViewClick, this);
23220
23221         this.store.on('beforeload', this.onBeforeLoad, this);
23222         this.store.on('load', this.onLoad, this);
23223         this.store.on('loadexception', this.onLoadException, this);
23224
23225         if(this.resizable){
23226             this.resizer = new Roo.Resizable(this.list,  {
23227                pinned:true, handles:'se'
23228             });
23229             this.resizer.on('resize', function(r, w, h){
23230                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23231                 this.listWidth = w;
23232                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23233                 this.restrictHeight();
23234             }, this);
23235             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23236         }
23237         if(!this.editable){
23238             this.editable = true;
23239             this.setEditable(false);
23240         }  
23241         
23242         
23243         if (typeof(this.events.add.listeners) != 'undefined') {
23244             
23245             this.addicon = this.wrap.createChild(
23246                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23247        
23248             this.addicon.on('click', function(e) {
23249                 this.fireEvent('add', this);
23250             }, this);
23251         }
23252         if (typeof(this.events.edit.listeners) != 'undefined') {
23253             
23254             this.editicon = this.wrap.createChild(
23255                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23256             if (this.addicon) {
23257                 this.editicon.setStyle('margin-left', '40px');
23258             }
23259             this.editicon.on('click', function(e) {
23260                 
23261                 // we fire even  if inothing is selected..
23262                 this.fireEvent('edit', this, this.lastData );
23263                 
23264             }, this);
23265         }
23266         
23267         
23268         
23269     },
23270
23271     // private
23272     initEvents : function(){
23273         Roo.form.ComboBox.superclass.initEvents.call(this);
23274
23275         this.keyNav = new Roo.KeyNav(this.el, {
23276             "up" : function(e){
23277                 this.inKeyMode = true;
23278                 this.selectPrev();
23279             },
23280
23281             "down" : function(e){
23282                 if(!this.isExpanded()){
23283                     this.onTriggerClick();
23284                 }else{
23285                     this.inKeyMode = true;
23286                     this.selectNext();
23287                 }
23288             },
23289
23290             "enter" : function(e){
23291                 this.onViewClick();
23292                 //return true;
23293             },
23294
23295             "esc" : function(e){
23296                 this.collapse();
23297             },
23298
23299             "tab" : function(e){
23300                 this.onViewClick(false);
23301                 this.fireEvent("specialkey", this, e);
23302                 return true;
23303             },
23304
23305             scope : this,
23306
23307             doRelay : function(foo, bar, hname){
23308                 if(hname == 'down' || this.scope.isExpanded()){
23309                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23310                 }
23311                 return true;
23312             },
23313
23314             forceKeyDown: true
23315         });
23316         this.queryDelay = Math.max(this.queryDelay || 10,
23317                 this.mode == 'local' ? 10 : 250);
23318         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23319         if(this.typeAhead){
23320             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23321         }
23322         if(this.editable !== false){
23323             this.el.on("keyup", this.onKeyUp, this);
23324         }
23325         if(this.forceSelection){
23326             this.on('blur', this.doForce, this);
23327         }
23328     },
23329
23330     onDestroy : function(){
23331         if(this.view){
23332             this.view.setStore(null);
23333             this.view.el.removeAllListeners();
23334             this.view.el.remove();
23335             this.view.purgeListeners();
23336         }
23337         if(this.list){
23338             this.list.destroy();
23339         }
23340         if(this.store){
23341             this.store.un('beforeload', this.onBeforeLoad, this);
23342             this.store.un('load', this.onLoad, this);
23343             this.store.un('loadexception', this.onLoadException, this);
23344         }
23345         Roo.form.ComboBox.superclass.onDestroy.call(this);
23346     },
23347
23348     // private
23349     fireKey : function(e){
23350         if(e.isNavKeyPress() && !this.list.isVisible()){
23351             this.fireEvent("specialkey", this, e);
23352         }
23353     },
23354
23355     // private
23356     onResize: function(w, h){
23357         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23358         
23359         if(typeof w != 'number'){
23360             // we do not handle it!?!?
23361             return;
23362         }
23363         var tw = this.trigger.getWidth();
23364         tw += this.addicon ? this.addicon.getWidth() : 0;
23365         tw += this.editicon ? this.editicon.getWidth() : 0;
23366         var x = w - tw;
23367         this.el.setWidth( this.adjustWidth('input', x));
23368             
23369         this.trigger.setStyle('left', x+'px');
23370         
23371         if(this.list && this.listWidth === undefined){
23372             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23373             this.list.setWidth(lw);
23374             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23375         }
23376         
23377     
23378         
23379     },
23380
23381     /**
23382      * Allow or prevent the user from directly editing the field text.  If false is passed,
23383      * the user will only be able to select from the items defined in the dropdown list.  This method
23384      * is the runtime equivalent of setting the 'editable' config option at config time.
23385      * @param {Boolean} value True to allow the user to directly edit the field text
23386      */
23387     setEditable : function(value){
23388         if(value == this.editable){
23389             return;
23390         }
23391         this.editable = value;
23392         if(!value){
23393             this.el.dom.setAttribute('readOnly', true);
23394             this.el.on('mousedown', this.onTriggerClick,  this);
23395             this.el.addClass('x-combo-noedit');
23396         }else{
23397             this.el.dom.setAttribute('readOnly', false);
23398             this.el.un('mousedown', this.onTriggerClick,  this);
23399             this.el.removeClass('x-combo-noedit');
23400         }
23401     },
23402
23403     // private
23404     onBeforeLoad : function(){
23405         if(!this.hasFocus){
23406             return;
23407         }
23408         this.innerList.update(this.loadingText ?
23409                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23410         this.restrictHeight();
23411         this.selectedIndex = -1;
23412     },
23413
23414     // private
23415     onLoad : function(){
23416         if(!this.hasFocus){
23417             return;
23418         }
23419         if(this.store.getCount() > 0){
23420             this.expand();
23421             this.restrictHeight();
23422             if(this.lastQuery == this.allQuery){
23423                 if(this.editable){
23424                     this.el.dom.select();
23425                 }
23426                 if(!this.selectByValue(this.value, true)){
23427                     this.select(0, true);
23428                 }
23429             }else{
23430                 this.selectNext();
23431                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23432                     this.taTask.delay(this.typeAheadDelay);
23433                 }
23434             }
23435         }else{
23436             this.onEmptyResults();
23437         }
23438         //this.el.focus();
23439     },
23440     // private
23441     onLoadException : function()
23442     {
23443         this.collapse();
23444         Roo.log(this.store.reader.jsonData);
23445         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23446             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23447         }
23448         
23449         
23450     },
23451     // private
23452     onTypeAhead : function(){
23453         if(this.store.getCount() > 0){
23454             var r = this.store.getAt(0);
23455             var newValue = r.data[this.displayField];
23456             var len = newValue.length;
23457             var selStart = this.getRawValue().length;
23458             if(selStart != len){
23459                 this.setRawValue(newValue);
23460                 this.selectText(selStart, newValue.length);
23461             }
23462         }
23463     },
23464
23465     // private
23466     onSelect : function(record, index){
23467         if(this.fireEvent('beforeselect', this, record, index) !== false){
23468             this.setFromData(index > -1 ? record.data : false);
23469             this.collapse();
23470             this.fireEvent('select', this, record, index);
23471         }
23472     },
23473
23474     /**
23475      * Returns the currently selected field value or empty string if no value is set.
23476      * @return {String} value The selected value
23477      */
23478     getValue : function(){
23479         if(this.valueField){
23480             return typeof this.value != 'undefined' ? this.value : '';
23481         }
23482         return Roo.form.ComboBox.superclass.getValue.call(this);
23483     },
23484
23485     /**
23486      * Clears any text/value currently set in the field
23487      */
23488     clearValue : function(){
23489         if(this.hiddenField){
23490             this.hiddenField.value = '';
23491         }
23492         this.value = '';
23493         this.setRawValue('');
23494         this.lastSelectionText = '';
23495         
23496     },
23497
23498     /**
23499      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23500      * will be displayed in the field.  If the value does not match the data value of an existing item,
23501      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23502      * Otherwise the field will be blank (although the value will still be set).
23503      * @param {String} value The value to match
23504      */
23505     setValue : function(v){
23506         var text = v;
23507         if(this.valueField){
23508             var r = this.findRecord(this.valueField, v);
23509             if(r){
23510                 text = r.data[this.displayField];
23511             }else if(this.valueNotFoundText !== undefined){
23512                 text = this.valueNotFoundText;
23513             }
23514         }
23515         this.lastSelectionText = text;
23516         if(this.hiddenField){
23517             this.hiddenField.value = v;
23518         }
23519         Roo.form.ComboBox.superclass.setValue.call(this, text);
23520         this.value = v;
23521     },
23522     /**
23523      * @property {Object} the last set data for the element
23524      */
23525     
23526     lastData : false,
23527     /**
23528      * Sets the value of the field based on a object which is related to the record format for the store.
23529      * @param {Object} value the value to set as. or false on reset?
23530      */
23531     setFromData : function(o){
23532         var dv = ''; // display value
23533         var vv = ''; // value value..
23534         this.lastData = o;
23535         if (this.displayField) {
23536             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23537         } else {
23538             // this is an error condition!!!
23539             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23540         }
23541         
23542         if(this.valueField){
23543             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23544         }
23545         if(this.hiddenField){
23546             this.hiddenField.value = vv;
23547             
23548             this.lastSelectionText = dv;
23549             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23550             this.value = vv;
23551             return;
23552         }
23553         // no hidden field.. - we store the value in 'value', but still display
23554         // display field!!!!
23555         this.lastSelectionText = dv;
23556         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23557         this.value = vv;
23558         
23559         
23560     },
23561     // private
23562     reset : function(){
23563         // overridden so that last data is reset..
23564         this.setValue(this.resetValue);
23565         this.clearInvalid();
23566         this.lastData = false;
23567         if (this.view) {
23568             this.view.clearSelections();
23569         }
23570     },
23571     // private
23572     findRecord : function(prop, value){
23573         var record;
23574         if(this.store.getCount() > 0){
23575             this.store.each(function(r){
23576                 if(r.data[prop] == value){
23577                     record = r;
23578                     return false;
23579                 }
23580                 return true;
23581             });
23582         }
23583         return record;
23584     },
23585     
23586     getName: function()
23587     {
23588         // returns hidden if it's set..
23589         if (!this.rendered) {return ''};
23590         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23591         
23592     },
23593     // private
23594     onViewMove : function(e, t){
23595         this.inKeyMode = false;
23596     },
23597
23598     // private
23599     onViewOver : function(e, t){
23600         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23601             return;
23602         }
23603         var item = this.view.findItemFromChild(t);
23604         if(item){
23605             var index = this.view.indexOf(item);
23606             this.select(index, false);
23607         }
23608     },
23609
23610     // private
23611     onViewClick : function(doFocus)
23612     {
23613         var index = this.view.getSelectedIndexes()[0];
23614         var r = this.store.getAt(index);
23615         if(r){
23616             this.onSelect(r, index);
23617         }
23618         if(doFocus !== false && !this.blockFocus){
23619             this.el.focus();
23620         }
23621     },
23622
23623     // private
23624     restrictHeight : function(){
23625         this.innerList.dom.style.height = '';
23626         var inner = this.innerList.dom;
23627         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23628         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23629         this.list.beginUpdate();
23630         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23631         this.list.alignTo(this.el, this.listAlign);
23632         this.list.endUpdate();
23633     },
23634
23635     // private
23636     onEmptyResults : function(){
23637         this.collapse();
23638     },
23639
23640     /**
23641      * Returns true if the dropdown list is expanded, else false.
23642      */
23643     isExpanded : function(){
23644         return this.list.isVisible();
23645     },
23646
23647     /**
23648      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23649      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23650      * @param {String} value The data value of the item to select
23651      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23652      * selected item if it is not currently in view (defaults to true)
23653      * @return {Boolean} True if the value matched an item in the list, else false
23654      */
23655     selectByValue : function(v, scrollIntoView){
23656         if(v !== undefined && v !== null){
23657             var r = this.findRecord(this.valueField || this.displayField, v);
23658             if(r){
23659                 this.select(this.store.indexOf(r), scrollIntoView);
23660                 return true;
23661             }
23662         }
23663         return false;
23664     },
23665
23666     /**
23667      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23668      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23669      * @param {Number} index The zero-based index of the list item to select
23670      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23671      * selected item if it is not currently in view (defaults to true)
23672      */
23673     select : function(index, scrollIntoView){
23674         this.selectedIndex = index;
23675         this.view.select(index);
23676         if(scrollIntoView !== false){
23677             var el = this.view.getNode(index);
23678             if(el){
23679                 this.innerList.scrollChildIntoView(el, false);
23680             }
23681         }
23682     },
23683
23684     // private
23685     selectNext : function(){
23686         var ct = this.store.getCount();
23687         if(ct > 0){
23688             if(this.selectedIndex == -1){
23689                 this.select(0);
23690             }else if(this.selectedIndex < ct-1){
23691                 this.select(this.selectedIndex+1);
23692             }
23693         }
23694     },
23695
23696     // private
23697     selectPrev : function(){
23698         var ct = this.store.getCount();
23699         if(ct > 0){
23700             if(this.selectedIndex == -1){
23701                 this.select(0);
23702             }else if(this.selectedIndex != 0){
23703                 this.select(this.selectedIndex-1);
23704             }
23705         }
23706     },
23707
23708     // private
23709     onKeyUp : function(e){
23710         if(this.editable !== false && !e.isSpecialKey()){
23711             this.lastKey = e.getKey();
23712             this.dqTask.delay(this.queryDelay);
23713         }
23714     },
23715
23716     // private
23717     validateBlur : function(){
23718         return !this.list || !this.list.isVisible();   
23719     },
23720
23721     // private
23722     initQuery : function(){
23723         this.doQuery(this.getRawValue());
23724     },
23725
23726     // private
23727     doForce : function(){
23728         if(this.el.dom.value.length > 0){
23729             this.el.dom.value =
23730                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23731              
23732         }
23733     },
23734
23735     /**
23736      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23737      * query allowing the query action to be canceled if needed.
23738      * @param {String} query The SQL query to execute
23739      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23740      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23741      * saved in the current store (defaults to false)
23742      */
23743     doQuery : function(q, forceAll){
23744         if(q === undefined || q === null){
23745             q = '';
23746         }
23747         var qe = {
23748             query: q,
23749             forceAll: forceAll,
23750             combo: this,
23751             cancel:false
23752         };
23753         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23754             return false;
23755         }
23756         q = qe.query;
23757         forceAll = qe.forceAll;
23758         if(forceAll === true || (q.length >= this.minChars)){
23759             if(this.lastQuery != q || this.alwaysQuery){
23760                 this.lastQuery = q;
23761                 if(this.mode == 'local'){
23762                     this.selectedIndex = -1;
23763                     if(forceAll){
23764                         this.store.clearFilter();
23765                     }else{
23766                         this.store.filter(this.displayField, q);
23767                     }
23768                     this.onLoad();
23769                 }else{
23770                     this.store.baseParams[this.queryParam] = q;
23771                     this.store.load({
23772                         params: this.getParams(q)
23773                     });
23774                     this.expand();
23775                 }
23776             }else{
23777                 this.selectedIndex = -1;
23778                 this.onLoad();   
23779             }
23780         }
23781     },
23782
23783     // private
23784     getParams : function(q){
23785         var p = {};
23786         //p[this.queryParam] = q;
23787         if(this.pageSize){
23788             p.start = 0;
23789             p.limit = this.pageSize;
23790         }
23791         return p;
23792     },
23793
23794     /**
23795      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23796      */
23797     collapse : function(){
23798         if(!this.isExpanded()){
23799             return;
23800         }
23801         this.list.hide();
23802         Roo.get(document).un('mousedown', this.collapseIf, this);
23803         Roo.get(document).un('mousewheel', this.collapseIf, this);
23804         if (!this.editable) {
23805             Roo.get(document).un('keydown', this.listKeyPress, this);
23806         }
23807         this.fireEvent('collapse', this);
23808     },
23809
23810     // private
23811     collapseIf : function(e){
23812         if(!e.within(this.wrap) && !e.within(this.list)){
23813             this.collapse();
23814         }
23815     },
23816
23817     /**
23818      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23819      */
23820     expand : function(){
23821         if(this.isExpanded() || !this.hasFocus){
23822             return;
23823         }
23824         this.list.alignTo(this.el, this.listAlign);
23825         this.list.show();
23826         Roo.get(document).on('mousedown', this.collapseIf, this);
23827         Roo.get(document).on('mousewheel', this.collapseIf, this);
23828         if (!this.editable) {
23829             Roo.get(document).on('keydown', this.listKeyPress, this);
23830         }
23831         
23832         this.fireEvent('expand', this);
23833     },
23834
23835     // private
23836     // Implements the default empty TriggerField.onTriggerClick function
23837     onTriggerClick : function(){
23838         if(this.disabled){
23839             return;
23840         }
23841         if(this.isExpanded()){
23842             this.collapse();
23843             if (!this.blockFocus) {
23844                 this.el.focus();
23845             }
23846             
23847         }else {
23848             this.hasFocus = true;
23849             if(this.triggerAction == 'all') {
23850                 this.doQuery(this.allQuery, true);
23851             } else {
23852                 this.doQuery(this.getRawValue());
23853             }
23854             if (!this.blockFocus) {
23855                 this.el.focus();
23856             }
23857         }
23858     },
23859     listKeyPress : function(e)
23860     {
23861         //Roo.log('listkeypress');
23862         // scroll to first matching element based on key pres..
23863         if (e.isSpecialKey()) {
23864             return false;
23865         }
23866         var k = String.fromCharCode(e.getKey()).toUpperCase();
23867         //Roo.log(k);
23868         var match  = false;
23869         var csel = this.view.getSelectedNodes();
23870         var cselitem = false;
23871         if (csel.length) {
23872             var ix = this.view.indexOf(csel[0]);
23873             cselitem  = this.store.getAt(ix);
23874             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23875                 cselitem = false;
23876             }
23877             
23878         }
23879         
23880         this.store.each(function(v) { 
23881             if (cselitem) {
23882                 // start at existing selection.
23883                 if (cselitem.id == v.id) {
23884                     cselitem = false;
23885                 }
23886                 return;
23887             }
23888                 
23889             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23890                 match = this.store.indexOf(v);
23891                 return false;
23892             }
23893         }, this);
23894         
23895         if (match === false) {
23896             return true; // no more action?
23897         }
23898         // scroll to?
23899         this.view.select(match);
23900         var sn = Roo.get(this.view.getSelectedNodes()[0]);
23901         sn.scrollIntoView(sn.dom.parentNode, false);
23902     }
23903
23904     /** 
23905     * @cfg {Boolean} grow 
23906     * @hide 
23907     */
23908     /** 
23909     * @cfg {Number} growMin 
23910     * @hide 
23911     */
23912     /** 
23913     * @cfg {Number} growMax 
23914     * @hide 
23915     */
23916     /**
23917      * @hide
23918      * @method autoSize
23919      */
23920 });/*
23921  * Copyright(c) 2010-2012, Roo J Solutions Limited
23922  *
23923  * Licence LGPL
23924  *
23925  */
23926
23927 /**
23928  * @class Roo.form.ComboBoxArray
23929  * @extends Roo.form.TextField
23930  * A facebook style adder... for lists of email / people / countries  etc...
23931  * pick multiple items from a combo box, and shows each one.
23932  *
23933  *  Fred [x]  Brian [x]  [Pick another |v]
23934  *
23935  *
23936  *  For this to work: it needs various extra information
23937  *    - normal combo problay has
23938  *      name, hiddenName
23939  *    + displayField, valueField
23940  *
23941  *    For our purpose...
23942  *
23943  *
23944  *   If we change from 'extends' to wrapping...
23945  *   
23946  *  
23947  *
23948  
23949  
23950  * @constructor
23951  * Create a new ComboBoxArray.
23952  * @param {Object} config Configuration options
23953  */
23954  
23955
23956 Roo.form.ComboBoxArray = function(config)
23957 {
23958     this.addEvents({
23959         /**
23960          * @event beforeremove
23961          * Fires before remove the value from the list
23962              * @param {Roo.form.ComboBoxArray} _self This combo box array
23963              * @param {Roo.form.ComboBoxArray.Item} item removed item
23964              */
23965         'beforeremove' : true,
23966         /**
23967          * @event remove
23968          * Fires when remove the value from the list
23969              * @param {Roo.form.ComboBoxArray} _self This combo box array
23970              * @param {Roo.form.ComboBoxArray.Item} item removed item
23971              */
23972         'remove' : true
23973         
23974         
23975     });
23976     
23977     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23978     
23979     this.items = new Roo.util.MixedCollection(false);
23980     
23981     // construct the child combo...
23982     
23983     
23984     
23985     
23986    
23987     
23988 }
23989
23990  
23991 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23992
23993     /**
23994      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23995      */
23996     
23997     lastData : false,
23998     
23999     // behavies liek a hiddne field
24000     inputType:      'hidden',
24001     /**
24002      * @cfg {Number} width The width of the box that displays the selected element
24003      */ 
24004     width:          300,
24005
24006     
24007     
24008     /**
24009      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24010      */
24011     name : false,
24012     /**
24013      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24014      */
24015     hiddenName : false,
24016     
24017     
24018     // private the array of items that are displayed..
24019     items  : false,
24020     // private - the hidden field el.
24021     hiddenEl : false,
24022     // private - the filed el..
24023     el : false,
24024     
24025     //validateValue : function() { return true; }, // all values are ok!
24026     //onAddClick: function() { },
24027     
24028     onRender : function(ct, position) 
24029     {
24030         
24031         // create the standard hidden element
24032         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24033         
24034         
24035         // give fake names to child combo;
24036         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24037         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24038         
24039         this.combo = Roo.factory(this.combo, Roo.form);
24040         this.combo.onRender(ct, position);
24041         if (typeof(this.combo.width) != 'undefined') {
24042             this.combo.onResize(this.combo.width,0);
24043         }
24044         
24045         this.combo.initEvents();
24046         
24047         // assigned so form know we need to do this..
24048         this.store          = this.combo.store;
24049         this.valueField     = this.combo.valueField;
24050         this.displayField   = this.combo.displayField ;
24051         
24052         
24053         this.combo.wrap.addClass('x-cbarray-grp');
24054         
24055         var cbwrap = this.combo.wrap.createChild(
24056             {tag: 'div', cls: 'x-cbarray-cb'},
24057             this.combo.el.dom
24058         );
24059         
24060              
24061         this.hiddenEl = this.combo.wrap.createChild({
24062             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24063         });
24064         this.el = this.combo.wrap.createChild({
24065             tag: 'input',  type:'hidden' , name: this.name, value : ''
24066         });
24067          //   this.el.dom.removeAttribute("name");
24068         
24069         
24070         this.outerWrap = this.combo.wrap;
24071         this.wrap = cbwrap;
24072         
24073         this.outerWrap.setWidth(this.width);
24074         this.outerWrap.dom.removeChild(this.el.dom);
24075         
24076         this.wrap.dom.appendChild(this.el.dom);
24077         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24078         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24079         
24080         this.combo.trigger.setStyle('position','relative');
24081         this.combo.trigger.setStyle('left', '0px');
24082         this.combo.trigger.setStyle('top', '2px');
24083         
24084         this.combo.el.setStyle('vertical-align', 'text-bottom');
24085         
24086         //this.trigger.setStyle('vertical-align', 'top');
24087         
24088         // this should use the code from combo really... on('add' ....)
24089         if (this.adder) {
24090             
24091         
24092             this.adder = this.outerWrap.createChild(
24093                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24094             var _t = this;
24095             this.adder.on('click', function(e) {
24096                 _t.fireEvent('adderclick', this, e);
24097             }, _t);
24098         }
24099         //var _t = this;
24100         //this.adder.on('click', this.onAddClick, _t);
24101         
24102         
24103         this.combo.on('select', function(cb, rec, ix) {
24104             this.addItem(rec.data);
24105             
24106             cb.setValue('');
24107             cb.el.dom.value = '';
24108             //cb.lastData = rec.data;
24109             // add to list
24110             
24111         }, this);
24112         
24113         
24114     },
24115     
24116     
24117     getName: function()
24118     {
24119         // returns hidden if it's set..
24120         if (!this.rendered) {return ''};
24121         return  this.hiddenName ? this.hiddenName : this.name;
24122         
24123     },
24124     
24125     
24126     onResize: function(w, h){
24127         
24128         return;
24129         // not sure if this is needed..
24130         //this.combo.onResize(w,h);
24131         
24132         if(typeof w != 'number'){
24133             // we do not handle it!?!?
24134             return;
24135         }
24136         var tw = this.combo.trigger.getWidth();
24137         tw += this.addicon ? this.addicon.getWidth() : 0;
24138         tw += this.editicon ? this.editicon.getWidth() : 0;
24139         var x = w - tw;
24140         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24141             
24142         this.combo.trigger.setStyle('left', '0px');
24143         
24144         if(this.list && this.listWidth === undefined){
24145             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24146             this.list.setWidth(lw);
24147             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24148         }
24149         
24150     
24151         
24152     },
24153     
24154     addItem: function(rec)
24155     {
24156         var valueField = this.combo.valueField;
24157         var displayField = this.combo.displayField;
24158         if (this.items.indexOfKey(rec[valueField]) > -1) {
24159             //console.log("GOT " + rec.data.id);
24160             return;
24161         }
24162         
24163         var x = new Roo.form.ComboBoxArray.Item({
24164             //id : rec[this.idField],
24165             data : rec,
24166             displayField : displayField ,
24167             tipField : displayField ,
24168             cb : this
24169         });
24170         // use the 
24171         this.items.add(rec[valueField],x);
24172         // add it before the element..
24173         this.updateHiddenEl();
24174         x.render(this.outerWrap, this.wrap.dom);
24175         // add the image handler..
24176     },
24177     
24178     updateHiddenEl : function()
24179     {
24180         this.validate();
24181         if (!this.hiddenEl) {
24182             return;
24183         }
24184         var ar = [];
24185         var idField = this.combo.valueField;
24186         
24187         this.items.each(function(f) {
24188             ar.push(f.data[idField]);
24189            
24190         });
24191         this.hiddenEl.dom.value = ar.join(',');
24192         this.validate();
24193     },
24194     
24195     reset : function()
24196     {
24197         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24198         this.items.each(function(f) {
24199            f.remove(); 
24200         });
24201         this.el.dom.value = '';
24202         if (this.hiddenEl) {
24203             this.hiddenEl.dom.value = '';
24204         }
24205         
24206     },
24207     getValue: function()
24208     {
24209         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24210     },
24211     setValue: function(v) // not a valid action - must use addItems..
24212     {
24213          
24214         this.reset();
24215         
24216         
24217         
24218         if (this.store.isLocal && (typeof(v) == 'string')) {
24219             // then we can use the store to find the values..
24220             // comma seperated at present.. this needs to allow JSON based encoding..
24221             this.hiddenEl.value  = v;
24222             var v_ar = [];
24223             Roo.each(v.split(','), function(k) {
24224                 Roo.log("CHECK " + this.valueField + ',' + k);
24225                 var li = this.store.query(this.valueField, k);
24226                 if (!li.length) {
24227                     return;
24228                 }
24229                 var add = {};
24230                 add[this.valueField] = k;
24231                 add[this.displayField] = li.item(0).data[this.displayField];
24232                 
24233                 this.addItem(add);
24234             }, this) 
24235              
24236         }
24237         if (typeof(v) == 'object' ) {
24238             // then let's assume it's an array of objects..
24239             Roo.each(v, function(l) {
24240                 this.addItem(l);
24241             }, this);
24242              
24243         }
24244         
24245         
24246     },
24247     setFromData: function(v)
24248     {
24249         // this recieves an object, if setValues is called.
24250         this.reset();
24251         this.el.dom.value = v[this.displayField];
24252         this.hiddenEl.dom.value = v[this.valueField];
24253         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24254             return;
24255         }
24256         var kv = v[this.valueField];
24257         var dv = v[this.displayField];
24258         kv = typeof(kv) != 'string' ? '' : kv;
24259         dv = typeof(dv) != 'string' ? '' : dv;
24260         
24261         
24262         var keys = kv.split(',');
24263         var display = dv.split(',');
24264         for (var i = 0 ; i < keys.length; i++) {
24265             
24266             add = {};
24267             add[this.valueField] = keys[i];
24268             add[this.displayField] = display[i];
24269             this.addItem(add);
24270         }
24271       
24272         
24273     },
24274     
24275     /**
24276      * Validates the combox array value
24277      * @return {Boolean} True if the value is valid, else false
24278      */
24279     validate : function(){
24280         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24281             this.clearInvalid();
24282             return true;
24283         }
24284         return false;
24285     },
24286     
24287     validateValue : function(value){
24288         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24289         
24290     },
24291     
24292     /*@
24293      * overide
24294      * 
24295      */
24296     isDirty : function() {
24297         if(this.disabled) {
24298             return false;
24299         }
24300         
24301         try {
24302             var d = Roo.decode(String(this.originalValue));
24303         } catch (e) {
24304             return String(this.getValue()) !== String(this.originalValue);
24305         }
24306         
24307         var originalValue = [];
24308         
24309         for (var i = 0; i < d.length; i++){
24310             originalValue.push(d[i][this.valueField]);
24311         }
24312         
24313         return String(this.getValue()) !== String(originalValue.join(','));
24314         
24315     }
24316     
24317 });
24318
24319
24320
24321 /**
24322  * @class Roo.form.ComboBoxArray.Item
24323  * @extends Roo.BoxComponent
24324  * A selected item in the list
24325  *  Fred [x]  Brian [x]  [Pick another |v]
24326  * 
24327  * @constructor
24328  * Create a new item.
24329  * @param {Object} config Configuration options
24330  */
24331  
24332 Roo.form.ComboBoxArray.Item = function(config) {
24333     config.id = Roo.id();
24334     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24335 }
24336
24337 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24338     data : {},
24339     cb: false,
24340     displayField : false,
24341     tipField : false,
24342     
24343     
24344     defaultAutoCreate : {
24345         tag: 'div',
24346         cls: 'x-cbarray-item',
24347         cn : [ 
24348             { tag: 'div' },
24349             {
24350                 tag: 'img',
24351                 width:16,
24352                 height : 16,
24353                 src : Roo.BLANK_IMAGE_URL ,
24354                 align: 'center'
24355             }
24356         ]
24357         
24358     },
24359     
24360  
24361     onRender : function(ct, position)
24362     {
24363         Roo.form.Field.superclass.onRender.call(this, ct, position);
24364         
24365         if(!this.el){
24366             var cfg = this.getAutoCreate();
24367             this.el = ct.createChild(cfg, position);
24368         }
24369         
24370         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24371         
24372         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24373             this.cb.renderer(this.data) :
24374             String.format('{0}',this.data[this.displayField]);
24375         
24376             
24377         this.el.child('div').dom.setAttribute('qtip',
24378                         String.format('{0}',this.data[this.tipField])
24379         );
24380         
24381         this.el.child('img').on('click', this.remove, this);
24382         
24383     },
24384    
24385     remove : function()
24386     {
24387         if(this.cb.disabled){
24388             return;
24389         }
24390         
24391         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24392             this.cb.items.remove(this);
24393             this.el.child('img').un('click', this.remove, this);
24394             this.el.remove();
24395             this.cb.updateHiddenEl();
24396
24397             this.cb.fireEvent('remove', this.cb, this);
24398         }
24399         
24400     }
24401 });/*
24402  * Based on:
24403  * Ext JS Library 1.1.1
24404  * Copyright(c) 2006-2007, Ext JS, LLC.
24405  *
24406  * Originally Released Under LGPL - original licence link has changed is not relivant.
24407  *
24408  * Fork - LGPL
24409  * <script type="text/javascript">
24410  */
24411 /**
24412  * @class Roo.form.Checkbox
24413  * @extends Roo.form.Field
24414  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24415  * @constructor
24416  * Creates a new Checkbox
24417  * @param {Object} config Configuration options
24418  */
24419 Roo.form.Checkbox = function(config){
24420     Roo.form.Checkbox.superclass.constructor.call(this, config);
24421     this.addEvents({
24422         /**
24423          * @event check
24424          * Fires when the checkbox is checked or unchecked.
24425              * @param {Roo.form.Checkbox} this This checkbox
24426              * @param {Boolean} checked The new checked value
24427              */
24428         check : true
24429     });
24430 };
24431
24432 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24433     /**
24434      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24435      */
24436     focusClass : undefined,
24437     /**
24438      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24439      */
24440     fieldClass: "x-form-field",
24441     /**
24442      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24443      */
24444     checked: false,
24445     /**
24446      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24447      * {tag: "input", type: "checkbox", autocomplete: "off"})
24448      */
24449     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24450     /**
24451      * @cfg {String} boxLabel The text that appears beside the checkbox
24452      */
24453     boxLabel : "",
24454     /**
24455      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24456      */  
24457     inputValue : '1',
24458     /**
24459      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24460      */
24461      valueOff: '0', // value when not checked..
24462
24463     actionMode : 'viewEl', 
24464     //
24465     // private
24466     itemCls : 'x-menu-check-item x-form-item',
24467     groupClass : 'x-menu-group-item',
24468     inputType : 'hidden',
24469     
24470     
24471     inSetChecked: false, // check that we are not calling self...
24472     
24473     inputElement: false, // real input element?
24474     basedOn: false, // ????
24475     
24476     isFormField: true, // not sure where this is needed!!!!
24477
24478     onResize : function(){
24479         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24480         if(!this.boxLabel){
24481             this.el.alignTo(this.wrap, 'c-c');
24482         }
24483     },
24484
24485     initEvents : function(){
24486         Roo.form.Checkbox.superclass.initEvents.call(this);
24487         this.el.on("click", this.onClick,  this);
24488         this.el.on("change", this.onClick,  this);
24489     },
24490
24491
24492     getResizeEl : function(){
24493         return this.wrap;
24494     },
24495
24496     getPositionEl : function(){
24497         return this.wrap;
24498     },
24499
24500     // private
24501     onRender : function(ct, position){
24502         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24503         /*
24504         if(this.inputValue !== undefined){
24505             this.el.dom.value = this.inputValue;
24506         }
24507         */
24508         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24509         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24510         var viewEl = this.wrap.createChild({ 
24511             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24512         this.viewEl = viewEl;   
24513         this.wrap.on('click', this.onClick,  this); 
24514         
24515         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24516         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24517         
24518         
24519         
24520         if(this.boxLabel){
24521             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24522         //    viewEl.on('click', this.onClick,  this); 
24523         }
24524         //if(this.checked){
24525             this.setChecked(this.checked);
24526         //}else{
24527             //this.checked = this.el.dom;
24528         //}
24529
24530     },
24531
24532     // private
24533     initValue : Roo.emptyFn,
24534
24535     /**
24536      * Returns the checked state of the checkbox.
24537      * @return {Boolean} True if checked, else false
24538      */
24539     getValue : function(){
24540         if(this.el){
24541             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24542         }
24543         return this.valueOff;
24544         
24545     },
24546
24547         // private
24548     onClick : function(){ 
24549         if (this.disabled) {
24550             return;
24551         }
24552         this.setChecked(!this.checked);
24553
24554         //if(this.el.dom.checked != this.checked){
24555         //    this.setValue(this.el.dom.checked);
24556        // }
24557     },
24558
24559     /**
24560      * Sets the checked state of the checkbox.
24561      * On is always based on a string comparison between inputValue and the param.
24562      * @param {Boolean/String} value - the value to set 
24563      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24564      */
24565     setValue : function(v,suppressEvent){
24566         
24567         
24568         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24569         //if(this.el && this.el.dom){
24570         //    this.el.dom.checked = this.checked;
24571         //    this.el.dom.defaultChecked = this.checked;
24572         //}
24573         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24574         //this.fireEvent("check", this, this.checked);
24575     },
24576     // private..
24577     setChecked : function(state,suppressEvent)
24578     {
24579         if (this.inSetChecked) {
24580             this.checked = state;
24581             return;
24582         }
24583         
24584     
24585         if(this.wrap){
24586             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24587         }
24588         this.checked = state;
24589         if(suppressEvent !== true){
24590             this.fireEvent('check', this, state);
24591         }
24592         this.inSetChecked = true;
24593         this.el.dom.value = state ? this.inputValue : this.valueOff;
24594         this.inSetChecked = false;
24595         
24596     },
24597     // handle setting of hidden value by some other method!!?!?
24598     setFromHidden: function()
24599     {
24600         if(!this.el){
24601             return;
24602         }
24603         //console.log("SET FROM HIDDEN");
24604         //alert('setFrom hidden');
24605         this.setValue(this.el.dom.value);
24606     },
24607     
24608     onDestroy : function()
24609     {
24610         if(this.viewEl){
24611             Roo.get(this.viewEl).remove();
24612         }
24613          
24614         Roo.form.Checkbox.superclass.onDestroy.call(this);
24615     }
24616
24617 });/*
24618  * Based on:
24619  * Ext JS Library 1.1.1
24620  * Copyright(c) 2006-2007, Ext JS, LLC.
24621  *
24622  * Originally Released Under LGPL - original licence link has changed is not relivant.
24623  *
24624  * Fork - LGPL
24625  * <script type="text/javascript">
24626  */
24627  
24628 /**
24629  * @class Roo.form.Radio
24630  * @extends Roo.form.Checkbox
24631  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24632  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24633  * @constructor
24634  * Creates a new Radio
24635  * @param {Object} config Configuration options
24636  */
24637 Roo.form.Radio = function(){
24638     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24639 };
24640 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24641     inputType: 'radio',
24642
24643     /**
24644      * If this radio is part of a group, it will return the selected value
24645      * @return {String}
24646      */
24647     getGroupValue : function(){
24648         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24649     },
24650     
24651     
24652     onRender : function(ct, position){
24653         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24654         
24655         if(this.inputValue !== undefined){
24656             this.el.dom.value = this.inputValue;
24657         }
24658          
24659         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24660         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24661         //var viewEl = this.wrap.createChild({ 
24662         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24663         //this.viewEl = viewEl;   
24664         //this.wrap.on('click', this.onClick,  this); 
24665         
24666         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24667         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24668         
24669         
24670         
24671         if(this.boxLabel){
24672             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24673         //    viewEl.on('click', this.onClick,  this); 
24674         }
24675          if(this.checked){
24676             this.el.dom.checked =   'checked' ;
24677         }
24678          
24679     } 
24680     
24681     
24682 });//<script type="text/javascript">
24683
24684 /*
24685  * Based  Ext JS Library 1.1.1
24686  * Copyright(c) 2006-2007, Ext JS, LLC.
24687  * LGPL
24688  *
24689  */
24690  
24691 /**
24692  * @class Roo.HtmlEditorCore
24693  * @extends Roo.Component
24694  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24695  *
24696  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24697  */
24698
24699 Roo.HtmlEditorCore = function(config){
24700     
24701     
24702     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24703     
24704     
24705     this.addEvents({
24706         /**
24707          * @event initialize
24708          * Fires when the editor is fully initialized (including the iframe)
24709          * @param {Roo.HtmlEditorCore} this
24710          */
24711         initialize: true,
24712         /**
24713          * @event activate
24714          * Fires when the editor is first receives the focus. Any insertion must wait
24715          * until after this event.
24716          * @param {Roo.HtmlEditorCore} this
24717          */
24718         activate: true,
24719          /**
24720          * @event beforesync
24721          * Fires before the textarea is updated with content from the editor iframe. Return false
24722          * to cancel the sync.
24723          * @param {Roo.HtmlEditorCore} this
24724          * @param {String} html
24725          */
24726         beforesync: true,
24727          /**
24728          * @event beforepush
24729          * Fires before the iframe editor is updated with content from the textarea. Return false
24730          * to cancel the push.
24731          * @param {Roo.HtmlEditorCore} this
24732          * @param {String} html
24733          */
24734         beforepush: true,
24735          /**
24736          * @event sync
24737          * Fires when the textarea is updated with content from the editor iframe.
24738          * @param {Roo.HtmlEditorCore} this
24739          * @param {String} html
24740          */
24741         sync: true,
24742          /**
24743          * @event push
24744          * Fires when the iframe editor is updated with content from the textarea.
24745          * @param {Roo.HtmlEditorCore} this
24746          * @param {String} html
24747          */
24748         push: true,
24749         
24750         /**
24751          * @event editorevent
24752          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24753          * @param {Roo.HtmlEditorCore} this
24754          */
24755         editorevent: true
24756         
24757     });
24758     
24759     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24760     
24761     // defaults : white / black...
24762     this.applyBlacklists();
24763     
24764     
24765     
24766 };
24767
24768
24769 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24770
24771
24772      /**
24773      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24774      */
24775     
24776     owner : false,
24777     
24778      /**
24779      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24780      *                        Roo.resizable.
24781      */
24782     resizable : false,
24783      /**
24784      * @cfg {Number} height (in pixels)
24785      */   
24786     height: 300,
24787    /**
24788      * @cfg {Number} width (in pixels)
24789      */   
24790     width: 500,
24791     
24792     /**
24793      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24794      * 
24795      */
24796     stylesheets: false,
24797     
24798     // id of frame..
24799     frameId: false,
24800     
24801     // private properties
24802     validationEvent : false,
24803     deferHeight: true,
24804     initialized : false,
24805     activated : false,
24806     sourceEditMode : false,
24807     onFocus : Roo.emptyFn,
24808     iframePad:3,
24809     hideMode:'offsets',
24810     
24811     clearUp: true,
24812     
24813     // blacklist + whitelisted elements..
24814     black: false,
24815     white: false,
24816      
24817     
24818
24819     /**
24820      * Protected method that will not generally be called directly. It
24821      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24822      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24823      */
24824     getDocMarkup : function(){
24825         // body styles..
24826         var st = '';
24827         
24828         // inherit styels from page...?? 
24829         if (this.stylesheets === false) {
24830             
24831             Roo.get(document.head).select('style').each(function(node) {
24832                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24833             });
24834             
24835             Roo.get(document.head).select('link').each(function(node) { 
24836                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24837             });
24838             
24839         } else if (!this.stylesheets.length) {
24840                 // simple..
24841                 st = '<style type="text/css">' +
24842                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24843                    '</style>';
24844         } else { 
24845             
24846         }
24847         
24848         st +=  '<style type="text/css">' +
24849             'IMG { cursor: pointer } ' +
24850         '</style>';
24851
24852         
24853         return '<html><head>' + st  +
24854             //<style type="text/css">' +
24855             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24856             //'</style>' +
24857             ' </head><body class="roo-htmleditor-body"></body></html>';
24858     },
24859
24860     // private
24861     onRender : function(ct, position)
24862     {
24863         var _t = this;
24864         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24865         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24866         
24867         
24868         this.el.dom.style.border = '0 none';
24869         this.el.dom.setAttribute('tabIndex', -1);
24870         this.el.addClass('x-hidden hide');
24871         
24872         
24873         
24874         if(Roo.isIE){ // fix IE 1px bogus margin
24875             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24876         }
24877        
24878         
24879         this.frameId = Roo.id();
24880         
24881          
24882         
24883         var iframe = this.owner.wrap.createChild({
24884             tag: 'iframe',
24885             cls: 'form-control', // bootstrap..
24886             id: this.frameId,
24887             name: this.frameId,
24888             frameBorder : 'no',
24889             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24890         }, this.el
24891         );
24892         
24893         
24894         this.iframe = iframe.dom;
24895
24896          this.assignDocWin();
24897         
24898         this.doc.designMode = 'on';
24899        
24900         this.doc.open();
24901         this.doc.write(this.getDocMarkup());
24902         this.doc.close();
24903
24904         
24905         var task = { // must defer to wait for browser to be ready
24906             run : function(){
24907                 //console.log("run task?" + this.doc.readyState);
24908                 this.assignDocWin();
24909                 if(this.doc.body || this.doc.readyState == 'complete'){
24910                     try {
24911                         this.doc.designMode="on";
24912                     } catch (e) {
24913                         return;
24914                     }
24915                     Roo.TaskMgr.stop(task);
24916                     this.initEditor.defer(10, this);
24917                 }
24918             },
24919             interval : 10,
24920             duration: 10000,
24921             scope: this
24922         };
24923         Roo.TaskMgr.start(task);
24924
24925     },
24926
24927     // private
24928     onResize : function(w, h)
24929     {
24930          Roo.log('resize: ' +w + ',' + h );
24931         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24932         if(!this.iframe){
24933             return;
24934         }
24935         if(typeof w == 'number'){
24936             
24937             this.iframe.style.width = w + 'px';
24938         }
24939         if(typeof h == 'number'){
24940             
24941             this.iframe.style.height = h + 'px';
24942             if(this.doc){
24943                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24944             }
24945         }
24946         
24947     },
24948
24949     /**
24950      * Toggles the editor between standard and source edit mode.
24951      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24952      */
24953     toggleSourceEdit : function(sourceEditMode){
24954         
24955         this.sourceEditMode = sourceEditMode === true;
24956         
24957         if(this.sourceEditMode){
24958  
24959             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24960             
24961         }else{
24962             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24963             //this.iframe.className = '';
24964             this.deferFocus();
24965         }
24966         //this.setSize(this.owner.wrap.getSize());
24967         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24968     },
24969
24970     
24971   
24972
24973     /**
24974      * Protected method that will not generally be called directly. If you need/want
24975      * custom HTML cleanup, this is the method you should override.
24976      * @param {String} html The HTML to be cleaned
24977      * return {String} The cleaned HTML
24978      */
24979     cleanHtml : function(html){
24980         html = String(html);
24981         if(html.length > 5){
24982             if(Roo.isSafari){ // strip safari nonsense
24983                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24984             }
24985         }
24986         if(html == '&nbsp;'){
24987             html = '';
24988         }
24989         return html;
24990     },
24991
24992     /**
24993      * HTML Editor -> Textarea
24994      * Protected method that will not generally be called directly. Syncs the contents
24995      * of the editor iframe with the textarea.
24996      */
24997     syncValue : function(){
24998         if(this.initialized){
24999             var bd = (this.doc.body || this.doc.documentElement);
25000             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25001             var html = bd.innerHTML;
25002             if(Roo.isSafari){
25003                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25004                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25005                 if(m && m[1]){
25006                     html = '<div style="'+m[0]+'">' + html + '</div>';
25007                 }
25008             }
25009             html = this.cleanHtml(html);
25010             // fix up the special chars.. normaly like back quotes in word...
25011             // however we do not want to do this with chinese..
25012             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25013                 var cc = b.charCodeAt();
25014                 if (
25015                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25016                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25017                     (cc >= 0xf900 && cc < 0xfb00 )
25018                 ) {
25019                         return b;
25020                 }
25021                 return "&#"+cc+";" 
25022             });
25023             if(this.owner.fireEvent('beforesync', this, html) !== false){
25024                 this.el.dom.value = html;
25025                 this.owner.fireEvent('sync', this, html);
25026             }
25027         }
25028     },
25029
25030     /**
25031      * Protected method that will not generally be called directly. Pushes the value of the textarea
25032      * into the iframe editor.
25033      */
25034     pushValue : function(){
25035         if(this.initialized){
25036             var v = this.el.dom.value.trim();
25037             
25038 //            if(v.length < 1){
25039 //                v = '&#160;';
25040 //            }
25041             
25042             if(this.owner.fireEvent('beforepush', this, v) !== false){
25043                 var d = (this.doc.body || this.doc.documentElement);
25044                 d.innerHTML = v;
25045                 this.cleanUpPaste();
25046                 this.el.dom.value = d.innerHTML;
25047                 this.owner.fireEvent('push', this, v);
25048             }
25049         }
25050     },
25051
25052     // private
25053     deferFocus : function(){
25054         this.focus.defer(10, this);
25055     },
25056
25057     // doc'ed in Field
25058     focus : function(){
25059         if(this.win && !this.sourceEditMode){
25060             this.win.focus();
25061         }else{
25062             this.el.focus();
25063         }
25064     },
25065     
25066     assignDocWin: function()
25067     {
25068         var iframe = this.iframe;
25069         
25070          if(Roo.isIE){
25071             this.doc = iframe.contentWindow.document;
25072             this.win = iframe.contentWindow;
25073         } else {
25074 //            if (!Roo.get(this.frameId)) {
25075 //                return;
25076 //            }
25077 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25078 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25079             
25080             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25081                 return;
25082             }
25083             
25084             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25085             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25086         }
25087     },
25088     
25089     // private
25090     initEditor : function(){
25091         //console.log("INIT EDITOR");
25092         this.assignDocWin();
25093         
25094         
25095         
25096         this.doc.designMode="on";
25097         this.doc.open();
25098         this.doc.write(this.getDocMarkup());
25099         this.doc.close();
25100         
25101         var dbody = (this.doc.body || this.doc.documentElement);
25102         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25103         // this copies styles from the containing element into thsi one..
25104         // not sure why we need all of this..
25105         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25106         
25107         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25108         //ss['background-attachment'] = 'fixed'; // w3c
25109         dbody.bgProperties = 'fixed'; // ie
25110         //Roo.DomHelper.applyStyles(dbody, ss);
25111         Roo.EventManager.on(this.doc, {
25112             //'mousedown': this.onEditorEvent,
25113             'mouseup': this.onEditorEvent,
25114             'dblclick': this.onEditorEvent,
25115             'click': this.onEditorEvent,
25116             'keyup': this.onEditorEvent,
25117             buffer:100,
25118             scope: this
25119         });
25120         if(Roo.isGecko){
25121             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25122         }
25123         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25124             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25125         }
25126         this.initialized = true;
25127
25128         this.owner.fireEvent('initialize', this);
25129         this.pushValue();
25130     },
25131
25132     // private
25133     onDestroy : function(){
25134         
25135         
25136         
25137         if(this.rendered){
25138             
25139             //for (var i =0; i < this.toolbars.length;i++) {
25140             //    // fixme - ask toolbars for heights?
25141             //    this.toolbars[i].onDestroy();
25142            // }
25143             
25144             //this.wrap.dom.innerHTML = '';
25145             //this.wrap.remove();
25146         }
25147     },
25148
25149     // private
25150     onFirstFocus : function(){
25151         
25152         this.assignDocWin();
25153         
25154         
25155         this.activated = true;
25156          
25157     
25158         if(Roo.isGecko){ // prevent silly gecko errors
25159             this.win.focus();
25160             var s = this.win.getSelection();
25161             if(!s.focusNode || s.focusNode.nodeType != 3){
25162                 var r = s.getRangeAt(0);
25163                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25164                 r.collapse(true);
25165                 this.deferFocus();
25166             }
25167             try{
25168                 this.execCmd('useCSS', true);
25169                 this.execCmd('styleWithCSS', false);
25170             }catch(e){}
25171         }
25172         this.owner.fireEvent('activate', this);
25173     },
25174
25175     // private
25176     adjustFont: function(btn){
25177         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25178         //if(Roo.isSafari){ // safari
25179         //    adjust *= 2;
25180        // }
25181         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25182         if(Roo.isSafari){ // safari
25183             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25184             v =  (v < 10) ? 10 : v;
25185             v =  (v > 48) ? 48 : v;
25186             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25187             
25188         }
25189         
25190         
25191         v = Math.max(1, v+adjust);
25192         
25193         this.execCmd('FontSize', v  );
25194     },
25195
25196     onEditorEvent : function(e)
25197     {
25198         this.owner.fireEvent('editorevent', this, e);
25199       //  this.updateToolbar();
25200         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25201     },
25202
25203     insertTag : function(tg)
25204     {
25205         // could be a bit smarter... -> wrap the current selected tRoo..
25206         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25207             
25208             range = this.createRange(this.getSelection());
25209             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25210             wrappingNode.appendChild(range.extractContents());
25211             range.insertNode(wrappingNode);
25212
25213             return;
25214             
25215             
25216             
25217         }
25218         this.execCmd("formatblock",   tg);
25219         
25220     },
25221     
25222     insertText : function(txt)
25223     {
25224         
25225         
25226         var range = this.createRange();
25227         range.deleteContents();
25228                //alert(Sender.getAttribute('label'));
25229                
25230         range.insertNode(this.doc.createTextNode(txt));
25231     } ,
25232     
25233      
25234
25235     /**
25236      * Executes a Midas editor command on the editor document and performs necessary focus and
25237      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25238      * @param {String} cmd The Midas command
25239      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25240      */
25241     relayCmd : function(cmd, value){
25242         this.win.focus();
25243         this.execCmd(cmd, value);
25244         this.owner.fireEvent('editorevent', this);
25245         //this.updateToolbar();
25246         this.owner.deferFocus();
25247     },
25248
25249     /**
25250      * Executes a Midas editor command directly on the editor document.
25251      * For visual commands, you should use {@link #relayCmd} instead.
25252      * <b>This should only be called after the editor is initialized.</b>
25253      * @param {String} cmd The Midas command
25254      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25255      */
25256     execCmd : function(cmd, value){
25257         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25258         this.syncValue();
25259     },
25260  
25261  
25262    
25263     /**
25264      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25265      * to insert tRoo.
25266      * @param {String} text | dom node.. 
25267      */
25268     insertAtCursor : function(text)
25269     {
25270         
25271         
25272         
25273         if(!this.activated){
25274             return;
25275         }
25276         /*
25277         if(Roo.isIE){
25278             this.win.focus();
25279             var r = this.doc.selection.createRange();
25280             if(r){
25281                 r.collapse(true);
25282                 r.pasteHTML(text);
25283                 this.syncValue();
25284                 this.deferFocus();
25285             
25286             }
25287             return;
25288         }
25289         */
25290         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25291             this.win.focus();
25292             
25293             
25294             // from jquery ui (MIT licenced)
25295             var range, node;
25296             var win = this.win;
25297             
25298             if (win.getSelection && win.getSelection().getRangeAt) {
25299                 range = win.getSelection().getRangeAt(0);
25300                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25301                 range.insertNode(node);
25302             } else if (win.document.selection && win.document.selection.createRange) {
25303                 // no firefox support
25304                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25305                 win.document.selection.createRange().pasteHTML(txt);
25306             } else {
25307                 // no firefox support
25308                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25309                 this.execCmd('InsertHTML', txt);
25310             } 
25311             
25312             this.syncValue();
25313             
25314             this.deferFocus();
25315         }
25316     },
25317  // private
25318     mozKeyPress : function(e){
25319         if(e.ctrlKey){
25320             var c = e.getCharCode(), cmd;
25321           
25322             if(c > 0){
25323                 c = String.fromCharCode(c).toLowerCase();
25324                 switch(c){
25325                     case 'b':
25326                         cmd = 'bold';
25327                         break;
25328                     case 'i':
25329                         cmd = 'italic';
25330                         break;
25331                     
25332                     case 'u':
25333                         cmd = 'underline';
25334                         break;
25335                     
25336                     case 'v':
25337                         this.cleanUpPaste.defer(100, this);
25338                         return;
25339                         
25340                 }
25341                 if(cmd){
25342                     this.win.focus();
25343                     this.execCmd(cmd);
25344                     this.deferFocus();
25345                     e.preventDefault();
25346                 }
25347                 
25348             }
25349         }
25350     },
25351
25352     // private
25353     fixKeys : function(){ // load time branching for fastest keydown performance
25354         if(Roo.isIE){
25355             return function(e){
25356                 var k = e.getKey(), r;
25357                 if(k == e.TAB){
25358                     e.stopEvent();
25359                     r = this.doc.selection.createRange();
25360                     if(r){
25361                         r.collapse(true);
25362                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25363                         this.deferFocus();
25364                     }
25365                     return;
25366                 }
25367                 
25368                 if(k == e.ENTER){
25369                     r = this.doc.selection.createRange();
25370                     if(r){
25371                         var target = r.parentElement();
25372                         if(!target || target.tagName.toLowerCase() != 'li'){
25373                             e.stopEvent();
25374                             r.pasteHTML('<br />');
25375                             r.collapse(false);
25376                             r.select();
25377                         }
25378                     }
25379                 }
25380                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25381                     this.cleanUpPaste.defer(100, this);
25382                     return;
25383                 }
25384                 
25385                 
25386             };
25387         }else if(Roo.isOpera){
25388             return function(e){
25389                 var k = e.getKey();
25390                 if(k == e.TAB){
25391                     e.stopEvent();
25392                     this.win.focus();
25393                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25394                     this.deferFocus();
25395                 }
25396                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25397                     this.cleanUpPaste.defer(100, this);
25398                     return;
25399                 }
25400                 
25401             };
25402         }else if(Roo.isSafari){
25403             return function(e){
25404                 var k = e.getKey();
25405                 
25406                 if(k == e.TAB){
25407                     e.stopEvent();
25408                     this.execCmd('InsertText','\t');
25409                     this.deferFocus();
25410                     return;
25411                 }
25412                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25413                     this.cleanUpPaste.defer(100, this);
25414                     return;
25415                 }
25416                 
25417              };
25418         }
25419     }(),
25420     
25421     getAllAncestors: function()
25422     {
25423         var p = this.getSelectedNode();
25424         var a = [];
25425         if (!p) {
25426             a.push(p); // push blank onto stack..
25427             p = this.getParentElement();
25428         }
25429         
25430         
25431         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25432             a.push(p);
25433             p = p.parentNode;
25434         }
25435         a.push(this.doc.body);
25436         return a;
25437     },
25438     lastSel : false,
25439     lastSelNode : false,
25440     
25441     
25442     getSelection : function() 
25443     {
25444         this.assignDocWin();
25445         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25446     },
25447     
25448     getSelectedNode: function() 
25449     {
25450         // this may only work on Gecko!!!
25451         
25452         // should we cache this!!!!
25453         
25454         
25455         
25456          
25457         var range = this.createRange(this.getSelection()).cloneRange();
25458         
25459         if (Roo.isIE) {
25460             var parent = range.parentElement();
25461             while (true) {
25462                 var testRange = range.duplicate();
25463                 testRange.moveToElementText(parent);
25464                 if (testRange.inRange(range)) {
25465                     break;
25466                 }
25467                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25468                     break;
25469                 }
25470                 parent = parent.parentElement;
25471             }
25472             return parent;
25473         }
25474         
25475         // is ancestor a text element.
25476         var ac =  range.commonAncestorContainer;
25477         if (ac.nodeType == 3) {
25478             ac = ac.parentNode;
25479         }
25480         
25481         var ar = ac.childNodes;
25482          
25483         var nodes = [];
25484         var other_nodes = [];
25485         var has_other_nodes = false;
25486         for (var i=0;i<ar.length;i++) {
25487             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25488                 continue;
25489             }
25490             // fullly contained node.
25491             
25492             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25493                 nodes.push(ar[i]);
25494                 continue;
25495             }
25496             
25497             // probably selected..
25498             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25499                 other_nodes.push(ar[i]);
25500                 continue;
25501             }
25502             // outer..
25503             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25504                 continue;
25505             }
25506             
25507             
25508             has_other_nodes = true;
25509         }
25510         if (!nodes.length && other_nodes.length) {
25511             nodes= other_nodes;
25512         }
25513         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25514             return false;
25515         }
25516         
25517         return nodes[0];
25518     },
25519     createRange: function(sel)
25520     {
25521         // this has strange effects when using with 
25522         // top toolbar - not sure if it's a great idea.
25523         //this.editor.contentWindow.focus();
25524         if (typeof sel != "undefined") {
25525             try {
25526                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25527             } catch(e) {
25528                 return this.doc.createRange();
25529             }
25530         } else {
25531             return this.doc.createRange();
25532         }
25533     },
25534     getParentElement: function()
25535     {
25536         
25537         this.assignDocWin();
25538         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25539         
25540         var range = this.createRange(sel);
25541          
25542         try {
25543             var p = range.commonAncestorContainer;
25544             while (p.nodeType == 3) { // text node
25545                 p = p.parentNode;
25546             }
25547             return p;
25548         } catch (e) {
25549             return null;
25550         }
25551     
25552     },
25553     /***
25554      *
25555      * Range intersection.. the hard stuff...
25556      *  '-1' = before
25557      *  '0' = hits..
25558      *  '1' = after.
25559      *         [ -- selected range --- ]
25560      *   [fail]                        [fail]
25561      *
25562      *    basically..
25563      *      if end is before start or  hits it. fail.
25564      *      if start is after end or hits it fail.
25565      *
25566      *   if either hits (but other is outside. - then it's not 
25567      *   
25568      *    
25569      **/
25570     
25571     
25572     // @see http://www.thismuchiknow.co.uk/?p=64.
25573     rangeIntersectsNode : function(range, node)
25574     {
25575         var nodeRange = node.ownerDocument.createRange();
25576         try {
25577             nodeRange.selectNode(node);
25578         } catch (e) {
25579             nodeRange.selectNodeContents(node);
25580         }
25581     
25582         var rangeStartRange = range.cloneRange();
25583         rangeStartRange.collapse(true);
25584     
25585         var rangeEndRange = range.cloneRange();
25586         rangeEndRange.collapse(false);
25587     
25588         var nodeStartRange = nodeRange.cloneRange();
25589         nodeStartRange.collapse(true);
25590     
25591         var nodeEndRange = nodeRange.cloneRange();
25592         nodeEndRange.collapse(false);
25593     
25594         return rangeStartRange.compareBoundaryPoints(
25595                  Range.START_TO_START, nodeEndRange) == -1 &&
25596                rangeEndRange.compareBoundaryPoints(
25597                  Range.START_TO_START, nodeStartRange) == 1;
25598         
25599          
25600     },
25601     rangeCompareNode : function(range, node)
25602     {
25603         var nodeRange = node.ownerDocument.createRange();
25604         try {
25605             nodeRange.selectNode(node);
25606         } catch (e) {
25607             nodeRange.selectNodeContents(node);
25608         }
25609         
25610         
25611         range.collapse(true);
25612     
25613         nodeRange.collapse(true);
25614      
25615         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25616         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25617          
25618         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25619         
25620         var nodeIsBefore   =  ss == 1;
25621         var nodeIsAfter    = ee == -1;
25622         
25623         if (nodeIsBefore && nodeIsAfter) {
25624             return 0; // outer
25625         }
25626         if (!nodeIsBefore && nodeIsAfter) {
25627             return 1; //right trailed.
25628         }
25629         
25630         if (nodeIsBefore && !nodeIsAfter) {
25631             return 2;  // left trailed.
25632         }
25633         // fully contined.
25634         return 3;
25635     },
25636
25637     // private? - in a new class?
25638     cleanUpPaste :  function()
25639     {
25640         // cleans up the whole document..
25641         Roo.log('cleanuppaste');
25642         
25643         this.cleanUpChildren(this.doc.body);
25644         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25645         if (clean != this.doc.body.innerHTML) {
25646             this.doc.body.innerHTML = clean;
25647         }
25648         
25649     },
25650     
25651     cleanWordChars : function(input) {// change the chars to hex code
25652         var he = Roo.HtmlEditorCore;
25653         
25654         var output = input;
25655         Roo.each(he.swapCodes, function(sw) { 
25656             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25657             
25658             output = output.replace(swapper, sw[1]);
25659         });
25660         
25661         return output;
25662     },
25663     
25664     
25665     cleanUpChildren : function (n)
25666     {
25667         if (!n.childNodes.length) {
25668             return;
25669         }
25670         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25671            this.cleanUpChild(n.childNodes[i]);
25672         }
25673     },
25674     
25675     
25676         
25677     
25678     cleanUpChild : function (node)
25679     {
25680         var ed = this;
25681         //console.log(node);
25682         if (node.nodeName == "#text") {
25683             // clean up silly Windows -- stuff?
25684             return; 
25685         }
25686         if (node.nodeName == "#comment") {
25687             node.parentNode.removeChild(node);
25688             // clean up silly Windows -- stuff?
25689             return; 
25690         }
25691         var lcname = node.tagName.toLowerCase();
25692         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25693         // whitelist of tags..
25694         
25695         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25696             // remove node.
25697             node.parentNode.removeChild(node);
25698             return;
25699             
25700         }
25701         
25702         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25703         
25704         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25705         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25706         
25707         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25708         //    remove_keep_children = true;
25709         //}
25710         
25711         if (remove_keep_children) {
25712             this.cleanUpChildren(node);
25713             // inserts everything just before this node...
25714             while (node.childNodes.length) {
25715                 var cn = node.childNodes[0];
25716                 node.removeChild(cn);
25717                 node.parentNode.insertBefore(cn, node);
25718             }
25719             node.parentNode.removeChild(node);
25720             return;
25721         }
25722         
25723         if (!node.attributes || !node.attributes.length) {
25724             this.cleanUpChildren(node);
25725             return;
25726         }
25727         
25728         function cleanAttr(n,v)
25729         {
25730             
25731             if (v.match(/^\./) || v.match(/^\//)) {
25732                 return;
25733             }
25734             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25735                 return;
25736             }
25737             if (v.match(/^#/)) {
25738                 return;
25739             }
25740 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25741             node.removeAttribute(n);
25742             
25743         }
25744         
25745         var cwhite = this.cwhite;
25746         var cblack = this.cblack;
25747             
25748         function cleanStyle(n,v)
25749         {
25750             if (v.match(/expression/)) { //XSS?? should we even bother..
25751                 node.removeAttribute(n);
25752                 return;
25753             }
25754             
25755             var parts = v.split(/;/);
25756             var clean = [];
25757             
25758             Roo.each(parts, function(p) {
25759                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25760                 if (!p.length) {
25761                     return true;
25762                 }
25763                 var l = p.split(':').shift().replace(/\s+/g,'');
25764                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25765                 
25766                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25767 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25768                     //node.removeAttribute(n);
25769                     return true;
25770                 }
25771                 //Roo.log()
25772                 // only allow 'c whitelisted system attributes'
25773                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25774 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25775                     //node.removeAttribute(n);
25776                     return true;
25777                 }
25778                 
25779                 
25780                  
25781                 
25782                 clean.push(p);
25783                 return true;
25784             });
25785             if (clean.length) { 
25786                 node.setAttribute(n, clean.join(';'));
25787             } else {
25788                 node.removeAttribute(n);
25789             }
25790             
25791         }
25792         
25793         
25794         for (var i = node.attributes.length-1; i > -1 ; i--) {
25795             var a = node.attributes[i];
25796             //console.log(a);
25797             
25798             if (a.name.toLowerCase().substr(0,2)=='on')  {
25799                 node.removeAttribute(a.name);
25800                 continue;
25801             }
25802             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25803                 node.removeAttribute(a.name);
25804                 continue;
25805             }
25806             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25807                 cleanAttr(a.name,a.value); // fixme..
25808                 continue;
25809             }
25810             if (a.name == 'style') {
25811                 cleanStyle(a.name,a.value);
25812                 continue;
25813             }
25814             /// clean up MS crap..
25815             // tecnically this should be a list of valid class'es..
25816             
25817             
25818             if (a.name == 'class') {
25819                 if (a.value.match(/^Mso/)) {
25820                     node.className = '';
25821                 }
25822                 
25823                 if (a.value.match(/body/)) {
25824                     node.className = '';
25825                 }
25826                 continue;
25827             }
25828             
25829             // style cleanup!?
25830             // class cleanup?
25831             
25832         }
25833         
25834         
25835         this.cleanUpChildren(node);
25836         
25837         
25838     },
25839     
25840     /**
25841      * Clean up MS wordisms...
25842      */
25843     cleanWord : function(node)
25844     {
25845         
25846         
25847         if (!node) {
25848             this.cleanWord(this.doc.body);
25849             return;
25850         }
25851         if (node.nodeName == "#text") {
25852             // clean up silly Windows -- stuff?
25853             return; 
25854         }
25855         if (node.nodeName == "#comment") {
25856             node.parentNode.removeChild(node);
25857             // clean up silly Windows -- stuff?
25858             return; 
25859         }
25860         
25861         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25862             node.parentNode.removeChild(node);
25863             return;
25864         }
25865         
25866         // remove - but keep children..
25867         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25868             while (node.childNodes.length) {
25869                 var cn = node.childNodes[0];
25870                 node.removeChild(cn);
25871                 node.parentNode.insertBefore(cn, node);
25872             }
25873             node.parentNode.removeChild(node);
25874             this.iterateChildren(node, this.cleanWord);
25875             return;
25876         }
25877         // clean styles
25878         if (node.className.length) {
25879             
25880             var cn = node.className.split(/\W+/);
25881             var cna = [];
25882             Roo.each(cn, function(cls) {
25883                 if (cls.match(/Mso[a-zA-Z]+/)) {
25884                     return;
25885                 }
25886                 cna.push(cls);
25887             });
25888             node.className = cna.length ? cna.join(' ') : '';
25889             if (!cna.length) {
25890                 node.removeAttribute("class");
25891             }
25892         }
25893         
25894         if (node.hasAttribute("lang")) {
25895             node.removeAttribute("lang");
25896         }
25897         
25898         if (node.hasAttribute("style")) {
25899             
25900             var styles = node.getAttribute("style").split(";");
25901             var nstyle = [];
25902             Roo.each(styles, function(s) {
25903                 if (!s.match(/:/)) {
25904                     return;
25905                 }
25906                 var kv = s.split(":");
25907                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25908                     return;
25909                 }
25910                 // what ever is left... we allow.
25911                 nstyle.push(s);
25912             });
25913             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25914             if (!nstyle.length) {
25915                 node.removeAttribute('style');
25916             }
25917         }
25918         this.iterateChildren(node, this.cleanWord);
25919         
25920         
25921         
25922     },
25923     /**
25924      * iterateChildren of a Node, calling fn each time, using this as the scole..
25925      * @param {DomNode} node node to iterate children of.
25926      * @param {Function} fn method of this class to call on each item.
25927      */
25928     iterateChildren : function(node, fn)
25929     {
25930         if (!node.childNodes.length) {
25931                 return;
25932         }
25933         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25934            fn.call(this, node.childNodes[i])
25935         }
25936     },
25937     
25938     
25939     /**
25940      * cleanTableWidths.
25941      *
25942      * Quite often pasting from word etc.. results in tables with column and widths.
25943      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25944      *
25945      */
25946     cleanTableWidths : function(node)
25947     {
25948          
25949          
25950         if (!node) {
25951             this.cleanTableWidths(this.doc.body);
25952             return;
25953         }
25954         
25955         // ignore list...
25956         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25957             return; 
25958         }
25959         Roo.log(node.tagName);
25960         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25961             this.iterateChildren(node, this.cleanTableWidths);
25962             return;
25963         }
25964         if (node.hasAttribute('width')) {
25965             node.removeAttribute('width');
25966         }
25967         
25968          
25969         if (node.hasAttribute("style")) {
25970             // pretty basic...
25971             
25972             var styles = node.getAttribute("style").split(";");
25973             var nstyle = [];
25974             Roo.each(styles, function(s) {
25975                 if (!s.match(/:/)) {
25976                     return;
25977                 }
25978                 var kv = s.split(":");
25979                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25980                     return;
25981                 }
25982                 // what ever is left... we allow.
25983                 nstyle.push(s);
25984             });
25985             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25986             if (!nstyle.length) {
25987                 node.removeAttribute('style');
25988             }
25989         }
25990         
25991         this.iterateChildren(node, this.cleanTableWidths);
25992         
25993         
25994     },
25995     
25996     
25997     
25998     
25999     domToHTML : function(currentElement, depth, nopadtext) {
26000         
26001         depth = depth || 0;
26002         nopadtext = nopadtext || false;
26003     
26004         if (!currentElement) {
26005             return this.domToHTML(this.doc.body);
26006         }
26007         
26008         //Roo.log(currentElement);
26009         var j;
26010         var allText = false;
26011         var nodeName = currentElement.nodeName;
26012         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26013         
26014         if  (nodeName == '#text') {
26015             
26016             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26017         }
26018         
26019         
26020         var ret = '';
26021         if (nodeName != 'BODY') {
26022              
26023             var i = 0;
26024             // Prints the node tagName, such as <A>, <IMG>, etc
26025             if (tagName) {
26026                 var attr = [];
26027                 for(i = 0; i < currentElement.attributes.length;i++) {
26028                     // quoting?
26029                     var aname = currentElement.attributes.item(i).name;
26030                     if (!currentElement.attributes.item(i).value.length) {
26031                         continue;
26032                     }
26033                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26034                 }
26035                 
26036                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26037             } 
26038             else {
26039                 
26040                 // eack
26041             }
26042         } else {
26043             tagName = false;
26044         }
26045         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26046             return ret;
26047         }
26048         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26049             nopadtext = true;
26050         }
26051         
26052         
26053         // Traverse the tree
26054         i = 0;
26055         var currentElementChild = currentElement.childNodes.item(i);
26056         var allText = true;
26057         var innerHTML  = '';
26058         lastnode = '';
26059         while (currentElementChild) {
26060             // Formatting code (indent the tree so it looks nice on the screen)
26061             var nopad = nopadtext;
26062             if (lastnode == 'SPAN') {
26063                 nopad  = true;
26064             }
26065             // text
26066             if  (currentElementChild.nodeName == '#text') {
26067                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26068                 toadd = nopadtext ? toadd : toadd.trim();
26069                 if (!nopad && toadd.length > 80) {
26070                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26071                 }
26072                 innerHTML  += toadd;
26073                 
26074                 i++;
26075                 currentElementChild = currentElement.childNodes.item(i);
26076                 lastNode = '';
26077                 continue;
26078             }
26079             allText = false;
26080             
26081             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26082                 
26083             // Recursively traverse the tree structure of the child node
26084             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26085             lastnode = currentElementChild.nodeName;
26086             i++;
26087             currentElementChild=currentElement.childNodes.item(i);
26088         }
26089         
26090         ret += innerHTML;
26091         
26092         if (!allText) {
26093                 // The remaining code is mostly for formatting the tree
26094             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26095         }
26096         
26097         
26098         if (tagName) {
26099             ret+= "</"+tagName+">";
26100         }
26101         return ret;
26102         
26103     },
26104         
26105     applyBlacklists : function()
26106     {
26107         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26108         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26109         
26110         this.white = [];
26111         this.black = [];
26112         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26113             if (b.indexOf(tag) > -1) {
26114                 return;
26115             }
26116             this.white.push(tag);
26117             
26118         }, this);
26119         
26120         Roo.each(w, function(tag) {
26121             if (b.indexOf(tag) > -1) {
26122                 return;
26123             }
26124             if (this.white.indexOf(tag) > -1) {
26125                 return;
26126             }
26127             this.white.push(tag);
26128             
26129         }, this);
26130         
26131         
26132         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26133             if (w.indexOf(tag) > -1) {
26134                 return;
26135             }
26136             this.black.push(tag);
26137             
26138         }, this);
26139         
26140         Roo.each(b, function(tag) {
26141             if (w.indexOf(tag) > -1) {
26142                 return;
26143             }
26144             if (this.black.indexOf(tag) > -1) {
26145                 return;
26146             }
26147             this.black.push(tag);
26148             
26149         }, this);
26150         
26151         
26152         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26153         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26154         
26155         this.cwhite = [];
26156         this.cblack = [];
26157         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26158             if (b.indexOf(tag) > -1) {
26159                 return;
26160             }
26161             this.cwhite.push(tag);
26162             
26163         }, this);
26164         
26165         Roo.each(w, function(tag) {
26166             if (b.indexOf(tag) > -1) {
26167                 return;
26168             }
26169             if (this.cwhite.indexOf(tag) > -1) {
26170                 return;
26171             }
26172             this.cwhite.push(tag);
26173             
26174         }, this);
26175         
26176         
26177         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26178             if (w.indexOf(tag) > -1) {
26179                 return;
26180             }
26181             this.cblack.push(tag);
26182             
26183         }, this);
26184         
26185         Roo.each(b, function(tag) {
26186             if (w.indexOf(tag) > -1) {
26187                 return;
26188             }
26189             if (this.cblack.indexOf(tag) > -1) {
26190                 return;
26191             }
26192             this.cblack.push(tag);
26193             
26194         }, this);
26195     },
26196     
26197     setStylesheets : function(stylesheets)
26198     {
26199         if(typeof(stylesheets) == 'string'){
26200             Roo.get(this.iframe.contentDocument.head).createChild({
26201                 tag : 'link',
26202                 rel : 'stylesheet',
26203                 type : 'text/css',
26204                 href : stylesheets
26205             });
26206             
26207             return;
26208         }
26209         var _this = this;
26210      
26211         Roo.each(stylesheets, function(s) {
26212             if(!s.length){
26213                 return;
26214             }
26215             
26216             Roo.get(_this.iframe.contentDocument.head).createChild({
26217                 tag : 'link',
26218                 rel : 'stylesheet',
26219                 type : 'text/css',
26220                 href : s
26221             });
26222         });
26223
26224         
26225     },
26226     
26227     removeStylesheets : function()
26228     {
26229         var _this = this;
26230         
26231         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26232             s.remove();
26233         });
26234     }
26235     
26236     // hide stuff that is not compatible
26237     /**
26238      * @event blur
26239      * @hide
26240      */
26241     /**
26242      * @event change
26243      * @hide
26244      */
26245     /**
26246      * @event focus
26247      * @hide
26248      */
26249     /**
26250      * @event specialkey
26251      * @hide
26252      */
26253     /**
26254      * @cfg {String} fieldClass @hide
26255      */
26256     /**
26257      * @cfg {String} focusClass @hide
26258      */
26259     /**
26260      * @cfg {String} autoCreate @hide
26261      */
26262     /**
26263      * @cfg {String} inputType @hide
26264      */
26265     /**
26266      * @cfg {String} invalidClass @hide
26267      */
26268     /**
26269      * @cfg {String} invalidText @hide
26270      */
26271     /**
26272      * @cfg {String} msgFx @hide
26273      */
26274     /**
26275      * @cfg {String} validateOnBlur @hide
26276      */
26277 });
26278
26279 Roo.HtmlEditorCore.white = [
26280         'area', 'br', 'img', 'input', 'hr', 'wbr',
26281         
26282        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26283        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26284        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26285        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26286        'table',   'ul',         'xmp', 
26287        
26288        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26289       'thead',   'tr', 
26290      
26291       'dir', 'menu', 'ol', 'ul', 'dl',
26292        
26293       'embed',  'object'
26294 ];
26295
26296
26297 Roo.HtmlEditorCore.black = [
26298     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26299         'applet', // 
26300         'base',   'basefont', 'bgsound', 'blink',  'body', 
26301         'frame',  'frameset', 'head',    'html',   'ilayer', 
26302         'iframe', 'layer',  'link',     'meta',    'object',   
26303         'script', 'style' ,'title',  'xml' // clean later..
26304 ];
26305 Roo.HtmlEditorCore.clean = [
26306     'script', 'style', 'title', 'xml'
26307 ];
26308 Roo.HtmlEditorCore.remove = [
26309     'font'
26310 ];
26311 // attributes..
26312
26313 Roo.HtmlEditorCore.ablack = [
26314     'on'
26315 ];
26316     
26317 Roo.HtmlEditorCore.aclean = [ 
26318     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26319 ];
26320
26321 // protocols..
26322 Roo.HtmlEditorCore.pwhite= [
26323         'http',  'https',  'mailto'
26324 ];
26325
26326 // white listed style attributes.
26327 Roo.HtmlEditorCore.cwhite= [
26328       //  'text-align', /// default is to allow most things..
26329       
26330          
26331 //        'font-size'//??
26332 ];
26333
26334 // black listed style attributes.
26335 Roo.HtmlEditorCore.cblack= [
26336       //  'font-size' -- this can be set by the project 
26337 ];
26338
26339
26340 Roo.HtmlEditorCore.swapCodes   =[ 
26341     [    8211, "--" ], 
26342     [    8212, "--" ], 
26343     [    8216,  "'" ],  
26344     [    8217, "'" ],  
26345     [    8220, '"' ],  
26346     [    8221, '"' ],  
26347     [    8226, "*" ],  
26348     [    8230, "..." ]
26349 ]; 
26350
26351     //<script type="text/javascript">
26352
26353 /*
26354  * Ext JS Library 1.1.1
26355  * Copyright(c) 2006-2007, Ext JS, LLC.
26356  * Licence LGPL
26357  * 
26358  */
26359  
26360  
26361 Roo.form.HtmlEditor = function(config){
26362     
26363     
26364     
26365     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26366     
26367     if (!this.toolbars) {
26368         this.toolbars = [];
26369     }
26370     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26371     
26372     
26373 };
26374
26375 /**
26376  * @class Roo.form.HtmlEditor
26377  * @extends Roo.form.Field
26378  * Provides a lightweight HTML Editor component.
26379  *
26380  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26381  * 
26382  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26383  * supported by this editor.</b><br/><br/>
26384  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26385  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26386  */
26387 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26388     /**
26389      * @cfg {Boolean} clearUp
26390      */
26391     clearUp : true,
26392       /**
26393      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26394      */
26395     toolbars : false,
26396    
26397      /**
26398      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26399      *                        Roo.resizable.
26400      */
26401     resizable : false,
26402      /**
26403      * @cfg {Number} height (in pixels)
26404      */   
26405     height: 300,
26406    /**
26407      * @cfg {Number} width (in pixels)
26408      */   
26409     width: 500,
26410     
26411     /**
26412      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26413      * 
26414      */
26415     stylesheets: false,
26416     
26417     
26418      /**
26419      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26420      * 
26421      */
26422     cblack: false,
26423     /**
26424      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26425      * 
26426      */
26427     cwhite: false,
26428     
26429      /**
26430      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26431      * 
26432      */
26433     black: false,
26434     /**
26435      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26436      * 
26437      */
26438     white: false,
26439     
26440     // id of frame..
26441     frameId: false,
26442     
26443     // private properties
26444     validationEvent : false,
26445     deferHeight: true,
26446     initialized : false,
26447     activated : false,
26448     
26449     onFocus : Roo.emptyFn,
26450     iframePad:3,
26451     hideMode:'offsets',
26452     
26453     actionMode : 'container', // defaults to hiding it...
26454     
26455     defaultAutoCreate : { // modified by initCompnoent..
26456         tag: "textarea",
26457         style:"width:500px;height:300px;",
26458         autocomplete: "new-password"
26459     },
26460
26461     // private
26462     initComponent : function(){
26463         this.addEvents({
26464             /**
26465              * @event initialize
26466              * Fires when the editor is fully initialized (including the iframe)
26467              * @param {HtmlEditor} this
26468              */
26469             initialize: true,
26470             /**
26471              * @event activate
26472              * Fires when the editor is first receives the focus. Any insertion must wait
26473              * until after this event.
26474              * @param {HtmlEditor} this
26475              */
26476             activate: true,
26477              /**
26478              * @event beforesync
26479              * Fires before the textarea is updated with content from the editor iframe. Return false
26480              * to cancel the sync.
26481              * @param {HtmlEditor} this
26482              * @param {String} html
26483              */
26484             beforesync: true,
26485              /**
26486              * @event beforepush
26487              * Fires before the iframe editor is updated with content from the textarea. Return false
26488              * to cancel the push.
26489              * @param {HtmlEditor} this
26490              * @param {String} html
26491              */
26492             beforepush: true,
26493              /**
26494              * @event sync
26495              * Fires when the textarea is updated with content from the editor iframe.
26496              * @param {HtmlEditor} this
26497              * @param {String} html
26498              */
26499             sync: true,
26500              /**
26501              * @event push
26502              * Fires when the iframe editor is updated with content from the textarea.
26503              * @param {HtmlEditor} this
26504              * @param {String} html
26505              */
26506             push: true,
26507              /**
26508              * @event editmodechange
26509              * Fires when the editor switches edit modes
26510              * @param {HtmlEditor} this
26511              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26512              */
26513             editmodechange: true,
26514             /**
26515              * @event editorevent
26516              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26517              * @param {HtmlEditor} this
26518              */
26519             editorevent: true,
26520             /**
26521              * @event firstfocus
26522              * Fires when on first focus - needed by toolbars..
26523              * @param {HtmlEditor} this
26524              */
26525             firstfocus: true,
26526             /**
26527              * @event autosave
26528              * Auto save the htmlEditor value as a file into Events
26529              * @param {HtmlEditor} this
26530              */
26531             autosave: true,
26532             /**
26533              * @event savedpreview
26534              * preview the saved version of htmlEditor
26535              * @param {HtmlEditor} this
26536              */
26537             savedpreview: true,
26538             
26539             /**
26540             * @event stylesheetsclick
26541             * Fires when press the Sytlesheets button
26542             * @param {Roo.HtmlEditorCore} this
26543             */
26544             stylesheetsclick: true
26545         });
26546         this.defaultAutoCreate =  {
26547             tag: "textarea",
26548             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26549             autocomplete: "new-password"
26550         };
26551     },
26552
26553     /**
26554      * Protected method that will not generally be called directly. It
26555      * is called when the editor creates its toolbar. Override this method if you need to
26556      * add custom toolbar buttons.
26557      * @param {HtmlEditor} editor
26558      */
26559     createToolbar : function(editor){
26560         Roo.log("create toolbars");
26561         if (!editor.toolbars || !editor.toolbars.length) {
26562             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26563         }
26564         
26565         for (var i =0 ; i < editor.toolbars.length;i++) {
26566             editor.toolbars[i] = Roo.factory(
26567                     typeof(editor.toolbars[i]) == 'string' ?
26568                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26569                 Roo.form.HtmlEditor);
26570             editor.toolbars[i].init(editor);
26571         }
26572          
26573         
26574     },
26575
26576      
26577     // private
26578     onRender : function(ct, position)
26579     {
26580         var _t = this;
26581         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26582         
26583         this.wrap = this.el.wrap({
26584             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26585         });
26586         
26587         this.editorcore.onRender(ct, position);
26588          
26589         if (this.resizable) {
26590             this.resizeEl = new Roo.Resizable(this.wrap, {
26591                 pinned : true,
26592                 wrap: true,
26593                 dynamic : true,
26594                 minHeight : this.height,
26595                 height: this.height,
26596                 handles : this.resizable,
26597                 width: this.width,
26598                 listeners : {
26599                     resize : function(r, w, h) {
26600                         _t.onResize(w,h); // -something
26601                     }
26602                 }
26603             });
26604             
26605         }
26606         this.createToolbar(this);
26607        
26608         
26609         if(!this.width){
26610             this.setSize(this.wrap.getSize());
26611         }
26612         if (this.resizeEl) {
26613             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26614             // should trigger onReize..
26615         }
26616         
26617         this.keyNav = new Roo.KeyNav(this.el, {
26618             
26619             "tab" : function(e){
26620                 e.preventDefault();
26621                 
26622                 var value = this.getValue();
26623                 
26624                 var start = this.el.dom.selectionStart;
26625                 var end = this.el.dom.selectionEnd;
26626                 
26627                 if(!e.shiftKey){
26628                     
26629                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26630                     this.el.dom.setSelectionRange(end + 1, end + 1);
26631                     return;
26632                 }
26633                 
26634                 var f = value.substring(0, start).split("\t");
26635                 
26636                 if(f.pop().length != 0){
26637                     return;
26638                 }
26639                 
26640                 this.setValue(f.join("\t") + value.substring(end));
26641                 this.el.dom.setSelectionRange(start - 1, start - 1);
26642                 
26643             },
26644             
26645             "home" : function(e){
26646                 e.preventDefault();
26647                 
26648                 var curr = this.el.dom.selectionStart;
26649                 var lines = this.getValue().split("\n");
26650                 
26651                 if(!lines.length){
26652                     return;
26653                 }
26654                 
26655                 if(e.ctrlKey){
26656                     this.el.dom.setSelectionRange(0, 0);
26657                     return;
26658                 }
26659                 
26660                 var pos = 0;
26661                 
26662                 for (var i = 0; i < lines.length;i++) {
26663                     pos += lines[i].length;
26664                     
26665                     if(i != 0){
26666                         pos += 1;
26667                     }
26668                     
26669                     if(pos < curr){
26670                         continue;
26671                     }
26672                     
26673                     pos -= lines[i].length;
26674                     
26675                     break;
26676                 }
26677                 
26678                 if(!e.shiftKey){
26679                     this.el.dom.setSelectionRange(pos, pos);
26680                     return;
26681                 }
26682                 
26683                 this.el.dom.selectionStart = pos;
26684                 this.el.dom.selectionEnd = curr;
26685             },
26686             
26687             "end" : function(e){
26688                 e.preventDefault();
26689                 
26690                 var curr = this.el.dom.selectionStart;
26691                 var lines = this.getValue().split("\n");
26692                 
26693                 if(!lines.length){
26694                     return;
26695                 }
26696                 
26697                 if(e.ctrlKey){
26698                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26699                     return;
26700                 }
26701                 
26702                 var pos = 0;
26703                 
26704                 for (var i = 0; i < lines.length;i++) {
26705                     
26706                     pos += lines[i].length;
26707                     
26708                     if(i != 0){
26709                         pos += 1;
26710                     }
26711                     
26712                     if(pos < curr){
26713                         continue;
26714                     }
26715                     
26716                     break;
26717                 }
26718                 
26719                 if(!e.shiftKey){
26720                     this.el.dom.setSelectionRange(pos, pos);
26721                     return;
26722                 }
26723                 
26724                 this.el.dom.selectionStart = curr;
26725                 this.el.dom.selectionEnd = pos;
26726             },
26727
26728             scope : this,
26729
26730             doRelay : function(foo, bar, hname){
26731                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26732             },
26733
26734             forceKeyDown: true
26735         });
26736         
26737 //        if(this.autosave && this.w){
26738 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26739 //        }
26740     },
26741
26742     // private
26743     onResize : function(w, h)
26744     {
26745         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26746         var ew = false;
26747         var eh = false;
26748         
26749         if(this.el ){
26750             if(typeof w == 'number'){
26751                 var aw = w - this.wrap.getFrameWidth('lr');
26752                 this.el.setWidth(this.adjustWidth('textarea', aw));
26753                 ew = aw;
26754             }
26755             if(typeof h == 'number'){
26756                 var tbh = 0;
26757                 for (var i =0; i < this.toolbars.length;i++) {
26758                     // fixme - ask toolbars for heights?
26759                     tbh += this.toolbars[i].tb.el.getHeight();
26760                     if (this.toolbars[i].footer) {
26761                         tbh += this.toolbars[i].footer.el.getHeight();
26762                     }
26763                 }
26764                 
26765                 
26766                 
26767                 
26768                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26769                 ah -= 5; // knock a few pixes off for look..
26770 //                Roo.log(ah);
26771                 this.el.setHeight(this.adjustWidth('textarea', ah));
26772                 var eh = ah;
26773             }
26774         }
26775         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26776         this.editorcore.onResize(ew,eh);
26777         
26778     },
26779
26780     /**
26781      * Toggles the editor between standard and source edit mode.
26782      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26783      */
26784     toggleSourceEdit : function(sourceEditMode)
26785     {
26786         this.editorcore.toggleSourceEdit(sourceEditMode);
26787         
26788         if(this.editorcore.sourceEditMode){
26789             Roo.log('editor - showing textarea');
26790             
26791 //            Roo.log('in');
26792 //            Roo.log(this.syncValue());
26793             this.editorcore.syncValue();
26794             this.el.removeClass('x-hidden');
26795             this.el.dom.removeAttribute('tabIndex');
26796             this.el.focus();
26797             
26798             for (var i = 0; i < this.toolbars.length; i++) {
26799                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26800                     this.toolbars[i].tb.hide();
26801                     this.toolbars[i].footer.hide();
26802                 }
26803             }
26804             
26805         }else{
26806             Roo.log('editor - hiding textarea');
26807 //            Roo.log('out')
26808 //            Roo.log(this.pushValue()); 
26809             this.editorcore.pushValue();
26810             
26811             this.el.addClass('x-hidden');
26812             this.el.dom.setAttribute('tabIndex', -1);
26813             
26814             for (var i = 0; i < this.toolbars.length; i++) {
26815                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26816                     this.toolbars[i].tb.show();
26817                     this.toolbars[i].footer.show();
26818                 }
26819             }
26820             
26821             //this.deferFocus();
26822         }
26823         
26824         this.setSize(this.wrap.getSize());
26825         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26826         
26827         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26828     },
26829  
26830     // private (for BoxComponent)
26831     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26832
26833     // private (for BoxComponent)
26834     getResizeEl : function(){
26835         return this.wrap;
26836     },
26837
26838     // private (for BoxComponent)
26839     getPositionEl : function(){
26840         return this.wrap;
26841     },
26842
26843     // private
26844     initEvents : function(){
26845         this.originalValue = this.getValue();
26846     },
26847
26848     /**
26849      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26850      * @method
26851      */
26852     markInvalid : Roo.emptyFn,
26853     /**
26854      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26855      * @method
26856      */
26857     clearInvalid : Roo.emptyFn,
26858
26859     setValue : function(v){
26860         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26861         this.editorcore.pushValue();
26862     },
26863
26864      
26865     // private
26866     deferFocus : function(){
26867         this.focus.defer(10, this);
26868     },
26869
26870     // doc'ed in Field
26871     focus : function(){
26872         this.editorcore.focus();
26873         
26874     },
26875       
26876
26877     // private
26878     onDestroy : function(){
26879         
26880         
26881         
26882         if(this.rendered){
26883             
26884             for (var i =0; i < this.toolbars.length;i++) {
26885                 // fixme - ask toolbars for heights?
26886                 this.toolbars[i].onDestroy();
26887             }
26888             
26889             this.wrap.dom.innerHTML = '';
26890             this.wrap.remove();
26891         }
26892     },
26893
26894     // private
26895     onFirstFocus : function(){
26896         //Roo.log("onFirstFocus");
26897         this.editorcore.onFirstFocus();
26898          for (var i =0; i < this.toolbars.length;i++) {
26899             this.toolbars[i].onFirstFocus();
26900         }
26901         
26902     },
26903     
26904     // private
26905     syncValue : function()
26906     {
26907         this.editorcore.syncValue();
26908     },
26909     
26910     pushValue : function()
26911     {
26912         this.editorcore.pushValue();
26913     },
26914     
26915     setStylesheets : function(stylesheets)
26916     {
26917         this.editorcore.setStylesheets(stylesheets);
26918     },
26919     
26920     removeStylesheets : function()
26921     {
26922         this.editorcore.removeStylesheets();
26923     }
26924      
26925     
26926     // hide stuff that is not compatible
26927     /**
26928      * @event blur
26929      * @hide
26930      */
26931     /**
26932      * @event change
26933      * @hide
26934      */
26935     /**
26936      * @event focus
26937      * @hide
26938      */
26939     /**
26940      * @event specialkey
26941      * @hide
26942      */
26943     /**
26944      * @cfg {String} fieldClass @hide
26945      */
26946     /**
26947      * @cfg {String} focusClass @hide
26948      */
26949     /**
26950      * @cfg {String} autoCreate @hide
26951      */
26952     /**
26953      * @cfg {String} inputType @hide
26954      */
26955     /**
26956      * @cfg {String} invalidClass @hide
26957      */
26958     /**
26959      * @cfg {String} invalidText @hide
26960      */
26961     /**
26962      * @cfg {String} msgFx @hide
26963      */
26964     /**
26965      * @cfg {String} validateOnBlur @hide
26966      */
26967 });
26968  
26969     // <script type="text/javascript">
26970 /*
26971  * Based on
26972  * Ext JS Library 1.1.1
26973  * Copyright(c) 2006-2007, Ext JS, LLC.
26974  *  
26975  
26976  */
26977
26978 /**
26979  * @class Roo.form.HtmlEditorToolbar1
26980  * Basic Toolbar
26981  * 
26982  * Usage:
26983  *
26984  new Roo.form.HtmlEditor({
26985     ....
26986     toolbars : [
26987         new Roo.form.HtmlEditorToolbar1({
26988             disable : { fonts: 1 , format: 1, ..., ... , ...],
26989             btns : [ .... ]
26990         })
26991     }
26992      
26993  * 
26994  * @cfg {Object} disable List of elements to disable..
26995  * @cfg {Array} btns List of additional buttons.
26996  * 
26997  * 
26998  * NEEDS Extra CSS? 
26999  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27000  */
27001  
27002 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27003 {
27004     
27005     Roo.apply(this, config);
27006     
27007     // default disabled, based on 'good practice'..
27008     this.disable = this.disable || {};
27009     Roo.applyIf(this.disable, {
27010         fontSize : true,
27011         colors : true,
27012         specialElements : true
27013     });
27014     
27015     
27016     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27017     // dont call parent... till later.
27018 }
27019
27020 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
27021     
27022     tb: false,
27023     
27024     rendered: false,
27025     
27026     editor : false,
27027     editorcore : false,
27028     /**
27029      * @cfg {Object} disable  List of toolbar elements to disable
27030          
27031      */
27032     disable : false,
27033     
27034     
27035      /**
27036      * @cfg {String} createLinkText The default text for the create link prompt
27037      */
27038     createLinkText : 'Please enter the URL for the link:',
27039     /**
27040      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27041      */
27042     defaultLinkValue : 'http:/'+'/',
27043    
27044     
27045       /**
27046      * @cfg {Array} fontFamilies An array of available font families
27047      */
27048     fontFamilies : [
27049         'Arial',
27050         'Courier New',
27051         'Tahoma',
27052         'Times New Roman',
27053         'Verdana'
27054     ],
27055     
27056     specialChars : [
27057            "&#169;",
27058           "&#174;",     
27059           "&#8482;",    
27060           "&#163;" ,    
27061          // "&#8212;",    
27062           "&#8230;",    
27063           "&#247;" ,    
27064         //  "&#225;" ,     ?? a acute?
27065            "&#8364;"    , //Euro
27066        //   "&#8220;"    ,
27067         //  "&#8221;"    ,
27068         //  "&#8226;"    ,
27069           "&#176;"  //   , // degrees
27070
27071          // "&#233;"     , // e ecute
27072          // "&#250;"     , // u ecute?
27073     ],
27074     
27075     specialElements : [
27076         {
27077             text: "Insert Table",
27078             xtype: 'MenuItem',
27079             xns : Roo.Menu,
27080             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27081                 
27082         },
27083         {    
27084             text: "Insert Image",
27085             xtype: 'MenuItem',
27086             xns : Roo.Menu,
27087             ihtml : '<img src="about:blank"/>'
27088             
27089         }
27090         
27091          
27092     ],
27093     
27094     
27095     inputElements : [ 
27096             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27097             "input:submit", "input:button", "select", "textarea", "label" ],
27098     formats : [
27099         ["p"] ,  
27100         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27101         ["pre"],[ "code"], 
27102         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27103         ['div'],['span']
27104     ],
27105     
27106     cleanStyles : [
27107         "font-size"
27108     ],
27109      /**
27110      * @cfg {String} defaultFont default font to use.
27111      */
27112     defaultFont: 'tahoma',
27113    
27114     fontSelect : false,
27115     
27116     
27117     formatCombo : false,
27118     
27119     init : function(editor)
27120     {
27121         this.editor = editor;
27122         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27123         var editorcore = this.editorcore;
27124         
27125         var _t = this;
27126         
27127         var fid = editorcore.frameId;
27128         var etb = this;
27129         function btn(id, toggle, handler){
27130             var xid = fid + '-'+ id ;
27131             return {
27132                 id : xid,
27133                 cmd : id,
27134                 cls : 'x-btn-icon x-edit-'+id,
27135                 enableToggle:toggle !== false,
27136                 scope: _t, // was editor...
27137                 handler:handler||_t.relayBtnCmd,
27138                 clickEvent:'mousedown',
27139                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27140                 tabIndex:-1
27141             };
27142         }
27143         
27144         
27145         
27146         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27147         this.tb = tb;
27148          // stop form submits
27149         tb.el.on('click', function(e){
27150             e.preventDefault(); // what does this do?
27151         });
27152
27153         if(!this.disable.font) { // && !Roo.isSafari){
27154             /* why no safari for fonts 
27155             editor.fontSelect = tb.el.createChild({
27156                 tag:'select',
27157                 tabIndex: -1,
27158                 cls:'x-font-select',
27159                 html: this.createFontOptions()
27160             });
27161             
27162             editor.fontSelect.on('change', function(){
27163                 var font = editor.fontSelect.dom.value;
27164                 editor.relayCmd('fontname', font);
27165                 editor.deferFocus();
27166             }, editor);
27167             
27168             tb.add(
27169                 editor.fontSelect.dom,
27170                 '-'
27171             );
27172             */
27173             
27174         };
27175         if(!this.disable.formats){
27176             this.formatCombo = new Roo.form.ComboBox({
27177                 store: new Roo.data.SimpleStore({
27178                     id : 'tag',
27179                     fields: ['tag'],
27180                     data : this.formats // from states.js
27181                 }),
27182                 blockFocus : true,
27183                 name : '',
27184                 //autoCreate : {tag: "div",  size: "20"},
27185                 displayField:'tag',
27186                 typeAhead: false,
27187                 mode: 'local',
27188                 editable : false,
27189                 triggerAction: 'all',
27190                 emptyText:'Add tag',
27191                 selectOnFocus:true,
27192                 width:135,
27193                 listeners : {
27194                     'select': function(c, r, i) {
27195                         editorcore.insertTag(r.get('tag'));
27196                         editor.focus();
27197                     }
27198                 }
27199
27200             });
27201             tb.addField(this.formatCombo);
27202             
27203         }
27204         
27205         if(!this.disable.format){
27206             tb.add(
27207                 btn('bold'),
27208                 btn('italic'),
27209                 btn('underline'),
27210                 btn('strikethrough')
27211             );
27212         };
27213         if(!this.disable.fontSize){
27214             tb.add(
27215                 '-',
27216                 
27217                 
27218                 btn('increasefontsize', false, editorcore.adjustFont),
27219                 btn('decreasefontsize', false, editorcore.adjustFont)
27220             );
27221         };
27222         
27223         
27224         if(!this.disable.colors){
27225             tb.add(
27226                 '-', {
27227                     id:editorcore.frameId +'-forecolor',
27228                     cls:'x-btn-icon x-edit-forecolor',
27229                     clickEvent:'mousedown',
27230                     tooltip: this.buttonTips['forecolor'] || undefined,
27231                     tabIndex:-1,
27232                     menu : new Roo.menu.ColorMenu({
27233                         allowReselect: true,
27234                         focus: Roo.emptyFn,
27235                         value:'000000',
27236                         plain:true,
27237                         selectHandler: function(cp, color){
27238                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27239                             editor.deferFocus();
27240                         },
27241                         scope: editorcore,
27242                         clickEvent:'mousedown'
27243                     })
27244                 }, {
27245                     id:editorcore.frameId +'backcolor',
27246                     cls:'x-btn-icon x-edit-backcolor',
27247                     clickEvent:'mousedown',
27248                     tooltip: this.buttonTips['backcolor'] || undefined,
27249                     tabIndex:-1,
27250                     menu : new Roo.menu.ColorMenu({
27251                         focus: Roo.emptyFn,
27252                         value:'FFFFFF',
27253                         plain:true,
27254                         allowReselect: true,
27255                         selectHandler: function(cp, color){
27256                             if(Roo.isGecko){
27257                                 editorcore.execCmd('useCSS', false);
27258                                 editorcore.execCmd('hilitecolor', color);
27259                                 editorcore.execCmd('useCSS', true);
27260                                 editor.deferFocus();
27261                             }else{
27262                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27263                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27264                                 editor.deferFocus();
27265                             }
27266                         },
27267                         scope:editorcore,
27268                         clickEvent:'mousedown'
27269                     })
27270                 }
27271             );
27272         };
27273         // now add all the items...
27274         
27275
27276         if(!this.disable.alignments){
27277             tb.add(
27278                 '-',
27279                 btn('justifyleft'),
27280                 btn('justifycenter'),
27281                 btn('justifyright')
27282             );
27283         };
27284
27285         //if(!Roo.isSafari){
27286             if(!this.disable.links){
27287                 tb.add(
27288                     '-',
27289                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27290                 );
27291             };
27292
27293             if(!this.disable.lists){
27294                 tb.add(
27295                     '-',
27296                     btn('insertorderedlist'),
27297                     btn('insertunorderedlist')
27298                 );
27299             }
27300             if(!this.disable.sourceEdit){
27301                 tb.add(
27302                     '-',
27303                     btn('sourceedit', true, function(btn){
27304                         this.toggleSourceEdit(btn.pressed);
27305                     })
27306                 );
27307             }
27308         //}
27309         
27310         var smenu = { };
27311         // special menu.. - needs to be tidied up..
27312         if (!this.disable.special) {
27313             smenu = {
27314                 text: "&#169;",
27315                 cls: 'x-edit-none',
27316                 
27317                 menu : {
27318                     items : []
27319                 }
27320             };
27321             for (var i =0; i < this.specialChars.length; i++) {
27322                 smenu.menu.items.push({
27323                     
27324                     html: this.specialChars[i],
27325                     handler: function(a,b) {
27326                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27327                         //editor.insertAtCursor(a.html);
27328                         
27329                     },
27330                     tabIndex:-1
27331                 });
27332             }
27333             
27334             
27335             tb.add(smenu);
27336             
27337             
27338         }
27339         
27340         var cmenu = { };
27341         if (!this.disable.cleanStyles) {
27342             cmenu = {
27343                 cls: 'x-btn-icon x-btn-clear',
27344                 
27345                 menu : {
27346                     items : []
27347                 }
27348             };
27349             for (var i =0; i < this.cleanStyles.length; i++) {
27350                 cmenu.menu.items.push({
27351                     actiontype : this.cleanStyles[i],
27352                     html: 'Remove ' + this.cleanStyles[i],
27353                     handler: function(a,b) {
27354 //                        Roo.log(a);
27355 //                        Roo.log(b);
27356                         var c = Roo.get(editorcore.doc.body);
27357                         c.select('[style]').each(function(s) {
27358                             s.dom.style.removeProperty(a.actiontype);
27359                         });
27360                         editorcore.syncValue();
27361                     },
27362                     tabIndex:-1
27363                 });
27364             }
27365              cmenu.menu.items.push({
27366                 actiontype : 'tablewidths',
27367                 html: 'Remove Table Widths',
27368                 handler: function(a,b) {
27369                     editorcore.cleanTableWidths();
27370                     editorcore.syncValue();
27371                 },
27372                 tabIndex:-1
27373             });
27374             cmenu.menu.items.push({
27375                 actiontype : 'word',
27376                 html: 'Remove MS Word Formating',
27377                 handler: function(a,b) {
27378                     editorcore.cleanWord();
27379                     editorcore.syncValue();
27380                 },
27381                 tabIndex:-1
27382             });
27383             
27384             cmenu.menu.items.push({
27385                 actiontype : 'all',
27386                 html: 'Remove All Styles',
27387                 handler: function(a,b) {
27388                     
27389                     var c = Roo.get(editorcore.doc.body);
27390                     c.select('[style]').each(function(s) {
27391                         s.dom.removeAttribute('style');
27392                     });
27393                     editorcore.syncValue();
27394                 },
27395                 tabIndex:-1
27396             });
27397             
27398             cmenu.menu.items.push({
27399                 actiontype : 'all',
27400                 html: 'Remove All CSS Classes',
27401                 handler: function(a,b) {
27402                     
27403                     var c = Roo.get(editorcore.doc.body);
27404                     c.select('[class]').each(function(s) {
27405                         s.dom.className = '';
27406                     });
27407                     editorcore.syncValue();
27408                 },
27409                 tabIndex:-1
27410             });
27411             
27412              cmenu.menu.items.push({
27413                 actiontype : 'tidy',
27414                 html: 'Tidy HTML Source',
27415                 handler: function(a,b) {
27416                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27417                     editorcore.syncValue();
27418                 },
27419                 tabIndex:-1
27420             });
27421             
27422             
27423             tb.add(cmenu);
27424         }
27425          
27426         if (!this.disable.specialElements) {
27427             var semenu = {
27428                 text: "Other;",
27429                 cls: 'x-edit-none',
27430                 menu : {
27431                     items : []
27432                 }
27433             };
27434             for (var i =0; i < this.specialElements.length; i++) {
27435                 semenu.menu.items.push(
27436                     Roo.apply({ 
27437                         handler: function(a,b) {
27438                             editor.insertAtCursor(this.ihtml);
27439                         }
27440                     }, this.specialElements[i])
27441                 );
27442                     
27443             }
27444             
27445             tb.add(semenu);
27446             
27447             
27448         }
27449          
27450         
27451         if (this.btns) {
27452             for(var i =0; i< this.btns.length;i++) {
27453                 var b = Roo.factory(this.btns[i],Roo.form);
27454                 b.cls =  'x-edit-none';
27455                 
27456                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27457                     b.cls += ' x-init-enable';
27458                 }
27459                 
27460                 b.scope = editorcore;
27461                 tb.add(b);
27462             }
27463         
27464         }
27465         
27466         
27467         
27468         // disable everything...
27469         
27470         this.tb.items.each(function(item){
27471             
27472            if(
27473                 item.id != editorcore.frameId+ '-sourceedit' && 
27474                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27475             ){
27476                 
27477                 item.disable();
27478             }
27479         });
27480         this.rendered = true;
27481         
27482         // the all the btns;
27483         editor.on('editorevent', this.updateToolbar, this);
27484         // other toolbars need to implement this..
27485         //editor.on('editmodechange', this.updateToolbar, this);
27486     },
27487     
27488     
27489     relayBtnCmd : function(btn) {
27490         this.editorcore.relayCmd(btn.cmd);
27491     },
27492     // private used internally
27493     createLink : function(){
27494         Roo.log("create link?");
27495         var url = prompt(this.createLinkText, this.defaultLinkValue);
27496         if(url && url != 'http:/'+'/'){
27497             this.editorcore.relayCmd('createlink', url);
27498         }
27499     },
27500
27501     
27502     /**
27503      * Protected method that will not generally be called directly. It triggers
27504      * a toolbar update by reading the markup state of the current selection in the editor.
27505      */
27506     updateToolbar: function(){
27507
27508         if(!this.editorcore.activated){
27509             this.editor.onFirstFocus();
27510             return;
27511         }
27512
27513         var btns = this.tb.items.map, 
27514             doc = this.editorcore.doc,
27515             frameId = this.editorcore.frameId;
27516
27517         if(!this.disable.font && !Roo.isSafari){
27518             /*
27519             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27520             if(name != this.fontSelect.dom.value){
27521                 this.fontSelect.dom.value = name;
27522             }
27523             */
27524         }
27525         if(!this.disable.format){
27526             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27527             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27528             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27529             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27530         }
27531         if(!this.disable.alignments){
27532             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27533             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27534             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27535         }
27536         if(!Roo.isSafari && !this.disable.lists){
27537             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27538             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27539         }
27540         
27541         var ans = this.editorcore.getAllAncestors();
27542         if (this.formatCombo) {
27543             
27544             
27545             var store = this.formatCombo.store;
27546             this.formatCombo.setValue("");
27547             for (var i =0; i < ans.length;i++) {
27548                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27549                     // select it..
27550                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27551                     break;
27552                 }
27553             }
27554         }
27555         
27556         
27557         
27558         // hides menus... - so this cant be on a menu...
27559         Roo.menu.MenuMgr.hideAll();
27560
27561         //this.editorsyncValue();
27562     },
27563    
27564     
27565     createFontOptions : function(){
27566         var buf = [], fs = this.fontFamilies, ff, lc;
27567         
27568         
27569         
27570         for(var i = 0, len = fs.length; i< len; i++){
27571             ff = fs[i];
27572             lc = ff.toLowerCase();
27573             buf.push(
27574                 '<option value="',lc,'" style="font-family:',ff,';"',
27575                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27576                     ff,
27577                 '</option>'
27578             );
27579         }
27580         return buf.join('');
27581     },
27582     
27583     toggleSourceEdit : function(sourceEditMode){
27584         
27585         Roo.log("toolbar toogle");
27586         if(sourceEditMode === undefined){
27587             sourceEditMode = !this.sourceEditMode;
27588         }
27589         this.sourceEditMode = sourceEditMode === true;
27590         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27591         // just toggle the button?
27592         if(btn.pressed !== this.sourceEditMode){
27593             btn.toggle(this.sourceEditMode);
27594             return;
27595         }
27596         
27597         if(sourceEditMode){
27598             Roo.log("disabling buttons");
27599             this.tb.items.each(function(item){
27600                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27601                     item.disable();
27602                 }
27603             });
27604           
27605         }else{
27606             Roo.log("enabling buttons");
27607             if(this.editorcore.initialized){
27608                 this.tb.items.each(function(item){
27609                     item.enable();
27610                 });
27611             }
27612             
27613         }
27614         Roo.log("calling toggole on editor");
27615         // tell the editor that it's been pressed..
27616         this.editor.toggleSourceEdit(sourceEditMode);
27617        
27618     },
27619      /**
27620      * Object collection of toolbar tooltips for the buttons in the editor. The key
27621      * is the command id associated with that button and the value is a valid QuickTips object.
27622      * For example:
27623 <pre><code>
27624 {
27625     bold : {
27626         title: 'Bold (Ctrl+B)',
27627         text: 'Make the selected text bold.',
27628         cls: 'x-html-editor-tip'
27629     },
27630     italic : {
27631         title: 'Italic (Ctrl+I)',
27632         text: 'Make the selected text italic.',
27633         cls: 'x-html-editor-tip'
27634     },
27635     ...
27636 </code></pre>
27637     * @type Object
27638      */
27639     buttonTips : {
27640         bold : {
27641             title: 'Bold (Ctrl+B)',
27642             text: 'Make the selected text bold.',
27643             cls: 'x-html-editor-tip'
27644         },
27645         italic : {
27646             title: 'Italic (Ctrl+I)',
27647             text: 'Make the selected text italic.',
27648             cls: 'x-html-editor-tip'
27649         },
27650         underline : {
27651             title: 'Underline (Ctrl+U)',
27652             text: 'Underline the selected text.',
27653             cls: 'x-html-editor-tip'
27654         },
27655         strikethrough : {
27656             title: 'Strikethrough',
27657             text: 'Strikethrough the selected text.',
27658             cls: 'x-html-editor-tip'
27659         },
27660         increasefontsize : {
27661             title: 'Grow Text',
27662             text: 'Increase the font size.',
27663             cls: 'x-html-editor-tip'
27664         },
27665         decreasefontsize : {
27666             title: 'Shrink Text',
27667             text: 'Decrease the font size.',
27668             cls: 'x-html-editor-tip'
27669         },
27670         backcolor : {
27671             title: 'Text Highlight Color',
27672             text: 'Change the background color of the selected text.',
27673             cls: 'x-html-editor-tip'
27674         },
27675         forecolor : {
27676             title: 'Font Color',
27677             text: 'Change the color of the selected text.',
27678             cls: 'x-html-editor-tip'
27679         },
27680         justifyleft : {
27681             title: 'Align Text Left',
27682             text: 'Align text to the left.',
27683             cls: 'x-html-editor-tip'
27684         },
27685         justifycenter : {
27686             title: 'Center Text',
27687             text: 'Center text in the editor.',
27688             cls: 'x-html-editor-tip'
27689         },
27690         justifyright : {
27691             title: 'Align Text Right',
27692             text: 'Align text to the right.',
27693             cls: 'x-html-editor-tip'
27694         },
27695         insertunorderedlist : {
27696             title: 'Bullet List',
27697             text: 'Start a bulleted list.',
27698             cls: 'x-html-editor-tip'
27699         },
27700         insertorderedlist : {
27701             title: 'Numbered List',
27702             text: 'Start a numbered list.',
27703             cls: 'x-html-editor-tip'
27704         },
27705         createlink : {
27706             title: 'Hyperlink',
27707             text: 'Make the selected text a hyperlink.',
27708             cls: 'x-html-editor-tip'
27709         },
27710         sourceedit : {
27711             title: 'Source Edit',
27712             text: 'Switch to source editing mode.',
27713             cls: 'x-html-editor-tip'
27714         }
27715     },
27716     // private
27717     onDestroy : function(){
27718         if(this.rendered){
27719             
27720             this.tb.items.each(function(item){
27721                 if(item.menu){
27722                     item.menu.removeAll();
27723                     if(item.menu.el){
27724                         item.menu.el.destroy();
27725                     }
27726                 }
27727                 item.destroy();
27728             });
27729              
27730         }
27731     },
27732     onFirstFocus: function() {
27733         this.tb.items.each(function(item){
27734            item.enable();
27735         });
27736     }
27737 });
27738
27739
27740
27741
27742 // <script type="text/javascript">
27743 /*
27744  * Based on
27745  * Ext JS Library 1.1.1
27746  * Copyright(c) 2006-2007, Ext JS, LLC.
27747  *  
27748  
27749  */
27750
27751  
27752 /**
27753  * @class Roo.form.HtmlEditor.ToolbarContext
27754  * Context Toolbar
27755  * 
27756  * Usage:
27757  *
27758  new Roo.form.HtmlEditor({
27759     ....
27760     toolbars : [
27761         { xtype: 'ToolbarStandard', styles : {} }
27762         { xtype: 'ToolbarContext', disable : {} }
27763     ]
27764 })
27765
27766      
27767  * 
27768  * @config : {Object} disable List of elements to disable.. (not done yet.)
27769  * @config : {Object} styles  Map of styles available.
27770  * 
27771  */
27772
27773 Roo.form.HtmlEditor.ToolbarContext = function(config)
27774 {
27775     
27776     Roo.apply(this, config);
27777     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27778     // dont call parent... till later.
27779     this.styles = this.styles || {};
27780 }
27781
27782  
27783
27784 Roo.form.HtmlEditor.ToolbarContext.types = {
27785     'IMG' : {
27786         width : {
27787             title: "Width",
27788             width: 40
27789         },
27790         height:  {
27791             title: "Height",
27792             width: 40
27793         },
27794         align: {
27795             title: "Align",
27796             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27797             width : 80
27798             
27799         },
27800         border: {
27801             title: "Border",
27802             width: 40
27803         },
27804         alt: {
27805             title: "Alt",
27806             width: 120
27807         },
27808         src : {
27809             title: "Src",
27810             width: 220
27811         }
27812         
27813     },
27814     'A' : {
27815         name : {
27816             title: "Name",
27817             width: 50
27818         },
27819         target:  {
27820             title: "Target",
27821             width: 120
27822         },
27823         href:  {
27824             title: "Href",
27825             width: 220
27826         } // border?
27827         
27828     },
27829     'TABLE' : {
27830         rows : {
27831             title: "Rows",
27832             width: 20
27833         },
27834         cols : {
27835             title: "Cols",
27836             width: 20
27837         },
27838         width : {
27839             title: "Width",
27840             width: 40
27841         },
27842         height : {
27843             title: "Height",
27844             width: 40
27845         },
27846         border : {
27847             title: "Border",
27848             width: 20
27849         }
27850     },
27851     'TD' : {
27852         width : {
27853             title: "Width",
27854             width: 40
27855         },
27856         height : {
27857             title: "Height",
27858             width: 40
27859         },   
27860         align: {
27861             title: "Align",
27862             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27863             width: 80
27864         },
27865         valign: {
27866             title: "Valign",
27867             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27868             width: 80
27869         },
27870         colspan: {
27871             title: "Colspan",
27872             width: 20
27873             
27874         },
27875          'font-family'  : {
27876             title : "Font",
27877             style : 'fontFamily',
27878             displayField: 'display',
27879             optname : 'font-family',
27880             width: 140
27881         }
27882     },
27883     'INPUT' : {
27884         name : {
27885             title: "name",
27886             width: 120
27887         },
27888         value : {
27889             title: "Value",
27890             width: 120
27891         },
27892         width : {
27893             title: "Width",
27894             width: 40
27895         }
27896     },
27897     'LABEL' : {
27898         'for' : {
27899             title: "For",
27900             width: 120
27901         }
27902     },
27903     'TEXTAREA' : {
27904           name : {
27905             title: "name",
27906             width: 120
27907         },
27908         rows : {
27909             title: "Rows",
27910             width: 20
27911         },
27912         cols : {
27913             title: "Cols",
27914             width: 20
27915         }
27916     },
27917     'SELECT' : {
27918         name : {
27919             title: "name",
27920             width: 120
27921         },
27922         selectoptions : {
27923             title: "Options",
27924             width: 200
27925         }
27926     },
27927     
27928     // should we really allow this??
27929     // should this just be 
27930     'BODY' : {
27931         title : {
27932             title: "Title",
27933             width: 200,
27934             disabled : true
27935         }
27936     },
27937     'SPAN' : {
27938         'font-family'  : {
27939             title : "Font",
27940             style : 'fontFamily',
27941             displayField: 'display',
27942             optname : 'font-family',
27943             width: 140
27944         }
27945     },
27946     'DIV' : {
27947         'font-family'  : {
27948             title : "Font",
27949             style : 'fontFamily',
27950             displayField: 'display',
27951             optname : 'font-family',
27952             width: 140
27953         }
27954     },
27955      'P' : {
27956         'font-family'  : {
27957             title : "Font",
27958             style : 'fontFamily',
27959             displayField: 'display',
27960             optname : 'font-family',
27961             width: 140
27962         }
27963     },
27964     
27965     '*' : {
27966         // empty..
27967     }
27968
27969 };
27970
27971 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27972 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27973
27974 Roo.form.HtmlEditor.ToolbarContext.options = {
27975         'font-family'  : [ 
27976                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27977                 [ 'Courier New', 'Courier New'],
27978                 [ 'Tahoma', 'Tahoma'],
27979                 [ 'Times New Roman,serif', 'Times'],
27980                 [ 'Verdana','Verdana' ]
27981         ]
27982 };
27983
27984 // fixme - these need to be configurable..
27985  
27986
27987 //Roo.form.HtmlEditor.ToolbarContext.types
27988
27989
27990 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27991     
27992     tb: false,
27993     
27994     rendered: false,
27995     
27996     editor : false,
27997     editorcore : false,
27998     /**
27999      * @cfg {Object} disable  List of toolbar elements to disable
28000          
28001      */
28002     disable : false,
28003     /**
28004      * @cfg {Object} styles List of styles 
28005      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28006      *
28007      * These must be defined in the page, so they get rendered correctly..
28008      * .headline { }
28009      * TD.underline { }
28010      * 
28011      */
28012     styles : false,
28013     
28014     options: false,
28015     
28016     toolbars : false,
28017     
28018     init : function(editor)
28019     {
28020         this.editor = editor;
28021         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28022         var editorcore = this.editorcore;
28023         
28024         var fid = editorcore.frameId;
28025         var etb = this;
28026         function btn(id, toggle, handler){
28027             var xid = fid + '-'+ id ;
28028             return {
28029                 id : xid,
28030                 cmd : id,
28031                 cls : 'x-btn-icon x-edit-'+id,
28032                 enableToggle:toggle !== false,
28033                 scope: editorcore, // was editor...
28034                 handler:handler||editorcore.relayBtnCmd,
28035                 clickEvent:'mousedown',
28036                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28037                 tabIndex:-1
28038             };
28039         }
28040         // create a new element.
28041         var wdiv = editor.wrap.createChild({
28042                 tag: 'div'
28043             }, editor.wrap.dom.firstChild.nextSibling, true);
28044         
28045         // can we do this more than once??
28046         
28047          // stop form submits
28048       
28049  
28050         // disable everything...
28051         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28052         this.toolbars = {};
28053            
28054         for (var i in  ty) {
28055           
28056             this.toolbars[i] = this.buildToolbar(ty[i],i);
28057         }
28058         this.tb = this.toolbars.BODY;
28059         this.tb.el.show();
28060         this.buildFooter();
28061         this.footer.show();
28062         editor.on('hide', function( ) { this.footer.hide() }, this);
28063         editor.on('show', function( ) { this.footer.show() }, this);
28064         
28065          
28066         this.rendered = true;
28067         
28068         // the all the btns;
28069         editor.on('editorevent', this.updateToolbar, this);
28070         // other toolbars need to implement this..
28071         //editor.on('editmodechange', this.updateToolbar, this);
28072     },
28073     
28074     
28075     
28076     /**
28077      * Protected method that will not generally be called directly. It triggers
28078      * a toolbar update by reading the markup state of the current selection in the editor.
28079      *
28080      * Note you can force an update by calling on('editorevent', scope, false)
28081      */
28082     updateToolbar: function(editor,ev,sel){
28083
28084         //Roo.log(ev);
28085         // capture mouse up - this is handy for selecting images..
28086         // perhaps should go somewhere else...
28087         if(!this.editorcore.activated){
28088              this.editor.onFirstFocus();
28089             return;
28090         }
28091         
28092         
28093         
28094         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28095         // selectNode - might want to handle IE?
28096         if (ev &&
28097             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28098             ev.target && ev.target.tagName == 'IMG') {
28099             // they have click on an image...
28100             // let's see if we can change the selection...
28101             sel = ev.target;
28102          
28103               var nodeRange = sel.ownerDocument.createRange();
28104             try {
28105                 nodeRange.selectNode(sel);
28106             } catch (e) {
28107                 nodeRange.selectNodeContents(sel);
28108             }
28109             //nodeRange.collapse(true);
28110             var s = this.editorcore.win.getSelection();
28111             s.removeAllRanges();
28112             s.addRange(nodeRange);
28113         }  
28114         
28115       
28116         var updateFooter = sel ? false : true;
28117         
28118         
28119         var ans = this.editorcore.getAllAncestors();
28120         
28121         // pick
28122         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28123         
28124         if (!sel) { 
28125             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28126             sel = sel ? sel : this.editorcore.doc.body;
28127             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28128             
28129         }
28130         // pick a menu that exists..
28131         var tn = sel.tagName.toUpperCase();
28132         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28133         
28134         tn = sel.tagName.toUpperCase();
28135         
28136         var lastSel = this.tb.selectedNode;
28137         
28138         this.tb.selectedNode = sel;
28139         
28140         // if current menu does not match..
28141         
28142         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28143                 
28144             this.tb.el.hide();
28145             ///console.log("show: " + tn);
28146             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28147             this.tb.el.show();
28148             // update name
28149             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28150             
28151             
28152             // update attributes
28153             if (this.tb.fields) {
28154                 this.tb.fields.each(function(e) {
28155                     if (e.stylename) {
28156                         e.setValue(sel.style[e.stylename]);
28157                         return;
28158                     } 
28159                    e.setValue(sel.getAttribute(e.attrname));
28160                 });
28161             }
28162             
28163             var hasStyles = false;
28164             for(var i in this.styles) {
28165                 hasStyles = true;
28166                 break;
28167             }
28168             
28169             // update styles
28170             if (hasStyles) { 
28171                 var st = this.tb.fields.item(0);
28172                 
28173                 st.store.removeAll();
28174                
28175                 
28176                 var cn = sel.className.split(/\s+/);
28177                 
28178                 var avs = [];
28179                 if (this.styles['*']) {
28180                     
28181                     Roo.each(this.styles['*'], function(v) {
28182                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28183                     });
28184                 }
28185                 if (this.styles[tn]) { 
28186                     Roo.each(this.styles[tn], function(v) {
28187                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28188                     });
28189                 }
28190                 
28191                 st.store.loadData(avs);
28192                 st.collapse();
28193                 st.setValue(cn);
28194             }
28195             // flag our selected Node.
28196             this.tb.selectedNode = sel;
28197            
28198            
28199             Roo.menu.MenuMgr.hideAll();
28200
28201         }
28202         
28203         if (!updateFooter) {
28204             //this.footDisp.dom.innerHTML = ''; 
28205             return;
28206         }
28207         // update the footer
28208         //
28209         var html = '';
28210         
28211         this.footerEls = ans.reverse();
28212         Roo.each(this.footerEls, function(a,i) {
28213             if (!a) { return; }
28214             html += html.length ? ' &gt; '  :  '';
28215             
28216             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28217             
28218         });
28219        
28220         // 
28221         var sz = this.footDisp.up('td').getSize();
28222         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28223         this.footDisp.dom.style.marginLeft = '5px';
28224         
28225         this.footDisp.dom.style.overflow = 'hidden';
28226         
28227         this.footDisp.dom.innerHTML = html;
28228             
28229         //this.editorsyncValue();
28230     },
28231      
28232     
28233    
28234        
28235     // private
28236     onDestroy : function(){
28237         if(this.rendered){
28238             
28239             this.tb.items.each(function(item){
28240                 if(item.menu){
28241                     item.menu.removeAll();
28242                     if(item.menu.el){
28243                         item.menu.el.destroy();
28244                     }
28245                 }
28246                 item.destroy();
28247             });
28248              
28249         }
28250     },
28251     onFirstFocus: function() {
28252         // need to do this for all the toolbars..
28253         this.tb.items.each(function(item){
28254            item.enable();
28255         });
28256     },
28257     buildToolbar: function(tlist, nm)
28258     {
28259         var editor = this.editor;
28260         var editorcore = this.editorcore;
28261          // create a new element.
28262         var wdiv = editor.wrap.createChild({
28263                 tag: 'div'
28264             }, editor.wrap.dom.firstChild.nextSibling, true);
28265         
28266        
28267         var tb = new Roo.Toolbar(wdiv);
28268         // add the name..
28269         
28270         tb.add(nm+ ":&nbsp;");
28271         
28272         var styles = [];
28273         for(var i in this.styles) {
28274             styles.push(i);
28275         }
28276         
28277         // styles...
28278         if (styles && styles.length) {
28279             
28280             // this needs a multi-select checkbox...
28281             tb.addField( new Roo.form.ComboBox({
28282                 store: new Roo.data.SimpleStore({
28283                     id : 'val',
28284                     fields: ['val', 'selected'],
28285                     data : [] 
28286                 }),
28287                 name : '-roo-edit-className',
28288                 attrname : 'className',
28289                 displayField: 'val',
28290                 typeAhead: false,
28291                 mode: 'local',
28292                 editable : false,
28293                 triggerAction: 'all',
28294                 emptyText:'Select Style',
28295                 selectOnFocus:true,
28296                 width: 130,
28297                 listeners : {
28298                     'select': function(c, r, i) {
28299                         // initial support only for on class per el..
28300                         tb.selectedNode.className =  r ? r.get('val') : '';
28301                         editorcore.syncValue();
28302                     }
28303                 }
28304     
28305             }));
28306         }
28307         
28308         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28309         var tbops = tbc.options;
28310         
28311         for (var i in tlist) {
28312             
28313             var item = tlist[i];
28314             tb.add(item.title + ":&nbsp;");
28315             
28316             
28317             //optname == used so you can configure the options available..
28318             var opts = item.opts ? item.opts : false;
28319             if (item.optname) {
28320                 opts = tbops[item.optname];
28321            
28322             }
28323             
28324             if (opts) {
28325                 // opts == pulldown..
28326                 tb.addField( new Roo.form.ComboBox({
28327                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28328                         id : 'val',
28329                         fields: ['val', 'display'],
28330                         data : opts  
28331                     }),
28332                     name : '-roo-edit-' + i,
28333                     attrname : i,
28334                     stylename : item.style ? item.style : false,
28335                     displayField: item.displayField ? item.displayField : 'val',
28336                     valueField :  'val',
28337                     typeAhead: false,
28338                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28339                     editable : false,
28340                     triggerAction: 'all',
28341                     emptyText:'Select',
28342                     selectOnFocus:true,
28343                     width: item.width ? item.width  : 130,
28344                     listeners : {
28345                         'select': function(c, r, i) {
28346                             if (c.stylename) {
28347                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28348                                 return;
28349                             }
28350                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28351                         }
28352                     }
28353
28354                 }));
28355                 continue;
28356                     
28357                  
28358                 
28359                 tb.addField( new Roo.form.TextField({
28360                     name: i,
28361                     width: 100,
28362                     //allowBlank:false,
28363                     value: ''
28364                 }));
28365                 continue;
28366             }
28367             tb.addField( new Roo.form.TextField({
28368                 name: '-roo-edit-' + i,
28369                 attrname : i,
28370                 
28371                 width: item.width,
28372                 //allowBlank:true,
28373                 value: '',
28374                 listeners: {
28375                     'change' : function(f, nv, ov) {
28376                         tb.selectedNode.setAttribute(f.attrname, nv);
28377                     }
28378                 }
28379             }));
28380              
28381         }
28382         
28383         var _this = this;
28384         
28385         if(nm == 'BODY'){
28386             tb.addSeparator();
28387         
28388             tb.addButton( {
28389                 text: 'Stylesheets',
28390
28391                 listeners : {
28392                     click : function ()
28393                     {
28394                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28395                     }
28396                 }
28397             });
28398         }
28399         
28400         tb.addFill();
28401         tb.addButton( {
28402             text: 'Remove Tag',
28403     
28404             listeners : {
28405                 click : function ()
28406                 {
28407                     // remove
28408                     // undo does not work.
28409                      
28410                     var sn = tb.selectedNode;
28411                     
28412                     var pn = sn.parentNode;
28413                     
28414                     var stn =  sn.childNodes[0];
28415                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28416                     while (sn.childNodes.length) {
28417                         var node = sn.childNodes[0];
28418                         sn.removeChild(node);
28419                         //Roo.log(node);
28420                         pn.insertBefore(node, sn);
28421                         
28422                     }
28423                     pn.removeChild(sn);
28424                     var range = editorcore.createRange();
28425         
28426                     range.setStart(stn,0);
28427                     range.setEnd(en,0); //????
28428                     //range.selectNode(sel);
28429                     
28430                     
28431                     var selection = editorcore.getSelection();
28432                     selection.removeAllRanges();
28433                     selection.addRange(range);
28434                     
28435                     
28436                     
28437                     //_this.updateToolbar(null, null, pn);
28438                     _this.updateToolbar(null, null, null);
28439                     _this.footDisp.dom.innerHTML = ''; 
28440                 }
28441             }
28442             
28443                     
28444                 
28445             
28446         });
28447         
28448         
28449         tb.el.on('click', function(e){
28450             e.preventDefault(); // what does this do?
28451         });
28452         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28453         tb.el.hide();
28454         tb.name = nm;
28455         // dont need to disable them... as they will get hidden
28456         return tb;
28457          
28458         
28459     },
28460     buildFooter : function()
28461     {
28462         
28463         var fel = this.editor.wrap.createChild();
28464         this.footer = new Roo.Toolbar(fel);
28465         // toolbar has scrolly on left / right?
28466         var footDisp= new Roo.Toolbar.Fill();
28467         var _t = this;
28468         this.footer.add(
28469             {
28470                 text : '&lt;',
28471                 xtype: 'Button',
28472                 handler : function() {
28473                     _t.footDisp.scrollTo('left',0,true)
28474                 }
28475             }
28476         );
28477         this.footer.add( footDisp );
28478         this.footer.add( 
28479             {
28480                 text : '&gt;',
28481                 xtype: 'Button',
28482                 handler : function() {
28483                     // no animation..
28484                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28485                 }
28486             }
28487         );
28488         var fel = Roo.get(footDisp.el);
28489         fel.addClass('x-editor-context');
28490         this.footDispWrap = fel; 
28491         this.footDispWrap.overflow  = 'hidden';
28492         
28493         this.footDisp = fel.createChild();
28494         this.footDispWrap.on('click', this.onContextClick, this)
28495         
28496         
28497     },
28498     onContextClick : function (ev,dom)
28499     {
28500         ev.preventDefault();
28501         var  cn = dom.className;
28502         //Roo.log(cn);
28503         if (!cn.match(/x-ed-loc-/)) {
28504             return;
28505         }
28506         var n = cn.split('-').pop();
28507         var ans = this.footerEls;
28508         var sel = ans[n];
28509         
28510          // pick
28511         var range = this.editorcore.createRange();
28512         
28513         range.selectNodeContents(sel);
28514         //range.selectNode(sel);
28515         
28516         
28517         var selection = this.editorcore.getSelection();
28518         selection.removeAllRanges();
28519         selection.addRange(range);
28520         
28521         
28522         
28523         this.updateToolbar(null, null, sel);
28524         
28525         
28526     }
28527     
28528     
28529     
28530     
28531     
28532 });
28533
28534
28535
28536
28537
28538 /*
28539  * Based on:
28540  * Ext JS Library 1.1.1
28541  * Copyright(c) 2006-2007, Ext JS, LLC.
28542  *
28543  * Originally Released Under LGPL - original licence link has changed is not relivant.
28544  *
28545  * Fork - LGPL
28546  * <script type="text/javascript">
28547  */
28548  
28549 /**
28550  * @class Roo.form.BasicForm
28551  * @extends Roo.util.Observable
28552  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28553  * @constructor
28554  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28555  * @param {Object} config Configuration options
28556  */
28557 Roo.form.BasicForm = function(el, config){
28558     this.allItems = [];
28559     this.childForms = [];
28560     Roo.apply(this, config);
28561     /*
28562      * The Roo.form.Field items in this form.
28563      * @type MixedCollection
28564      */
28565      
28566      
28567     this.items = new Roo.util.MixedCollection(false, function(o){
28568         return o.id || (o.id = Roo.id());
28569     });
28570     this.addEvents({
28571         /**
28572          * @event beforeaction
28573          * Fires before any action is performed. Return false to cancel the action.
28574          * @param {Form} this
28575          * @param {Action} action The action to be performed
28576          */
28577         beforeaction: true,
28578         /**
28579          * @event actionfailed
28580          * Fires when an action fails.
28581          * @param {Form} this
28582          * @param {Action} action The action that failed
28583          */
28584         actionfailed : true,
28585         /**
28586          * @event actioncomplete
28587          * Fires when an action is completed.
28588          * @param {Form} this
28589          * @param {Action} action The action that completed
28590          */
28591         actioncomplete : true
28592     });
28593     if(el){
28594         this.initEl(el);
28595     }
28596     Roo.form.BasicForm.superclass.constructor.call(this);
28597 };
28598
28599 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28600     /**
28601      * @cfg {String} method
28602      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28603      */
28604     /**
28605      * @cfg {DataReader} reader
28606      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28607      * This is optional as there is built-in support for processing JSON.
28608      */
28609     /**
28610      * @cfg {DataReader} errorReader
28611      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28612      * This is completely optional as there is built-in support for processing JSON.
28613      */
28614     /**
28615      * @cfg {String} url
28616      * The URL to use for form actions if one isn't supplied in the action options.
28617      */
28618     /**
28619      * @cfg {Boolean} fileUpload
28620      * Set to true if this form is a file upload.
28621      */
28622      
28623     /**
28624      * @cfg {Object} baseParams
28625      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28626      */
28627      /**
28628      
28629     /**
28630      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28631      */
28632     timeout: 30,
28633
28634     // private
28635     activeAction : null,
28636
28637     /**
28638      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28639      * or setValues() data instead of when the form was first created.
28640      */
28641     trackResetOnLoad : false,
28642     
28643     
28644     /**
28645      * childForms - used for multi-tab forms
28646      * @type {Array}
28647      */
28648     childForms : false,
28649     
28650     /**
28651      * allItems - full list of fields.
28652      * @type {Array}
28653      */
28654     allItems : false,
28655     
28656     /**
28657      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28658      * element by passing it or its id or mask the form itself by passing in true.
28659      * @type Mixed
28660      */
28661     waitMsgTarget : false,
28662
28663     // private
28664     initEl : function(el){
28665         this.el = Roo.get(el);
28666         this.id = this.el.id || Roo.id();
28667         this.el.on('submit', this.onSubmit, this);
28668         this.el.addClass('x-form');
28669     },
28670
28671     // private
28672     onSubmit : function(e){
28673         e.stopEvent();
28674     },
28675
28676     /**
28677      * Returns true if client-side validation on the form is successful.
28678      * @return Boolean
28679      */
28680     isValid : function(){
28681         var valid = true;
28682         this.items.each(function(f){
28683            if(!f.validate()){
28684                valid = false;
28685            }
28686         });
28687         return valid;
28688     },
28689
28690     /**
28691      * Returns true if any fields in this form have changed since their original load.
28692      * @return Boolean
28693      */
28694     isDirty : function(){
28695         var dirty = false;
28696         this.items.each(function(f){
28697            if(f.isDirty()){
28698                dirty = true;
28699                return false;
28700            }
28701         });
28702         return dirty;
28703     },
28704
28705     /**
28706      * Performs a predefined action (submit or load) or custom actions you define on this form.
28707      * @param {String} actionName The name of the action type
28708      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28709      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28710      * accept other config options):
28711      * <pre>
28712 Property          Type             Description
28713 ----------------  ---------------  ----------------------------------------------------------------------------------
28714 url               String           The url for the action (defaults to the form's url)
28715 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28716 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28717 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28718                                    validate the form on the client (defaults to false)
28719      * </pre>
28720      * @return {BasicForm} this
28721      */
28722     doAction : function(action, options){
28723         if(typeof action == 'string'){
28724             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28725         }
28726         if(this.fireEvent('beforeaction', this, action) !== false){
28727             this.beforeAction(action);
28728             action.run.defer(100, action);
28729         }
28730         return this;
28731     },
28732
28733     /**
28734      * Shortcut to do a submit action.
28735      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28736      * @return {BasicForm} this
28737      */
28738     submit : function(options){
28739         this.doAction('submit', options);
28740         return this;
28741     },
28742
28743     /**
28744      * Shortcut to do a load action.
28745      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28746      * @return {BasicForm} this
28747      */
28748     load : function(options){
28749         this.doAction('load', options);
28750         return this;
28751     },
28752
28753     /**
28754      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28755      * @param {Record} record The record to edit
28756      * @return {BasicForm} this
28757      */
28758     updateRecord : function(record){
28759         record.beginEdit();
28760         var fs = record.fields;
28761         fs.each(function(f){
28762             var field = this.findField(f.name);
28763             if(field){
28764                 record.set(f.name, field.getValue());
28765             }
28766         }, this);
28767         record.endEdit();
28768         return this;
28769     },
28770
28771     /**
28772      * Loads an Roo.data.Record into this form.
28773      * @param {Record} record The record to load
28774      * @return {BasicForm} this
28775      */
28776     loadRecord : function(record){
28777         this.setValues(record.data);
28778         return this;
28779     },
28780
28781     // private
28782     beforeAction : function(action){
28783         var o = action.options;
28784         
28785        
28786         if(this.waitMsgTarget === true){
28787             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28788         }else if(this.waitMsgTarget){
28789             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28790             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28791         }else {
28792             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28793         }
28794          
28795     },
28796
28797     // private
28798     afterAction : function(action, success){
28799         this.activeAction = null;
28800         var o = action.options;
28801         
28802         if(this.waitMsgTarget === true){
28803             this.el.unmask();
28804         }else if(this.waitMsgTarget){
28805             this.waitMsgTarget.unmask();
28806         }else{
28807             Roo.MessageBox.updateProgress(1);
28808             Roo.MessageBox.hide();
28809         }
28810          
28811         if(success){
28812             if(o.reset){
28813                 this.reset();
28814             }
28815             Roo.callback(o.success, o.scope, [this, action]);
28816             this.fireEvent('actioncomplete', this, action);
28817             
28818         }else{
28819             
28820             // failure condition..
28821             // we have a scenario where updates need confirming.
28822             // eg. if a locking scenario exists..
28823             // we look for { errors : { needs_confirm : true }} in the response.
28824             if (
28825                 (typeof(action.result) != 'undefined')  &&
28826                 (typeof(action.result.errors) != 'undefined')  &&
28827                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28828            ){
28829                 var _t = this;
28830                 Roo.MessageBox.confirm(
28831                     "Change requires confirmation",
28832                     action.result.errorMsg,
28833                     function(r) {
28834                         if (r != 'yes') {
28835                             return;
28836                         }
28837                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28838                     }
28839                     
28840                 );
28841                 
28842                 
28843                 
28844                 return;
28845             }
28846             
28847             Roo.callback(o.failure, o.scope, [this, action]);
28848             // show an error message if no failed handler is set..
28849             if (!this.hasListener('actionfailed')) {
28850                 Roo.MessageBox.alert("Error",
28851                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28852                         action.result.errorMsg :
28853                         "Saving Failed, please check your entries or try again"
28854                 );
28855             }
28856             
28857             this.fireEvent('actionfailed', this, action);
28858         }
28859         
28860     },
28861
28862     /**
28863      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28864      * @param {String} id The value to search for
28865      * @return Field
28866      */
28867     findField : function(id){
28868         var field = this.items.get(id);
28869         if(!field){
28870             this.items.each(function(f){
28871                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28872                     field = f;
28873                     return false;
28874                 }
28875             });
28876         }
28877         return field || null;
28878     },
28879
28880     /**
28881      * Add a secondary form to this one, 
28882      * Used to provide tabbed forms. One form is primary, with hidden values 
28883      * which mirror the elements from the other forms.
28884      * 
28885      * @param {Roo.form.Form} form to add.
28886      * 
28887      */
28888     addForm : function(form)
28889     {
28890        
28891         if (this.childForms.indexOf(form) > -1) {
28892             // already added..
28893             return;
28894         }
28895         this.childForms.push(form);
28896         var n = '';
28897         Roo.each(form.allItems, function (fe) {
28898             
28899             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28900             if (this.findField(n)) { // already added..
28901                 return;
28902             }
28903             var add = new Roo.form.Hidden({
28904                 name : n
28905             });
28906             add.render(this.el);
28907             
28908             this.add( add );
28909         }, this);
28910         
28911     },
28912     /**
28913      * Mark fields in this form invalid in bulk.
28914      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28915      * @return {BasicForm} this
28916      */
28917     markInvalid : function(errors){
28918         if(errors instanceof Array){
28919             for(var i = 0, len = errors.length; i < len; i++){
28920                 var fieldError = errors[i];
28921                 var f = this.findField(fieldError.id);
28922                 if(f){
28923                     f.markInvalid(fieldError.msg);
28924                 }
28925             }
28926         }else{
28927             var field, id;
28928             for(id in errors){
28929                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28930                     field.markInvalid(errors[id]);
28931                 }
28932             }
28933         }
28934         Roo.each(this.childForms || [], function (f) {
28935             f.markInvalid(errors);
28936         });
28937         
28938         return this;
28939     },
28940
28941     /**
28942      * Set values for fields in this form in bulk.
28943      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28944      * @return {BasicForm} this
28945      */
28946     setValues : function(values){
28947         if(values instanceof Array){ // array of objects
28948             for(var i = 0, len = values.length; i < len; i++){
28949                 var v = values[i];
28950                 var f = this.findField(v.id);
28951                 if(f){
28952                     f.setValue(v.value);
28953                     if(this.trackResetOnLoad){
28954                         f.originalValue = f.getValue();
28955                     }
28956                 }
28957             }
28958         }else{ // object hash
28959             var field, id;
28960             for(id in values){
28961                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28962                     
28963                     if (field.setFromData && 
28964                         field.valueField && 
28965                         field.displayField &&
28966                         // combos' with local stores can 
28967                         // be queried via setValue()
28968                         // to set their value..
28969                         (field.store && !field.store.isLocal)
28970                         ) {
28971                         // it's a combo
28972                         var sd = { };
28973                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28974                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28975                         field.setFromData(sd);
28976                         
28977                     } else {
28978                         field.setValue(values[id]);
28979                     }
28980                     
28981                     
28982                     if(this.trackResetOnLoad){
28983                         field.originalValue = field.getValue();
28984                     }
28985                 }
28986             }
28987         }
28988          
28989         Roo.each(this.childForms || [], function (f) {
28990             f.setValues(values);
28991         });
28992                 
28993         return this;
28994     },
28995
28996     /**
28997      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28998      * they are returned as an array.
28999      * @param {Boolean} asString
29000      * @return {Object}
29001      */
29002     getValues : function(asString){
29003         if (this.childForms) {
29004             // copy values from the child forms
29005             Roo.each(this.childForms, function (f) {
29006                 this.setValues(f.getValues());
29007             }, this);
29008         }
29009         
29010         
29011         
29012         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29013         if(asString === true){
29014             return fs;
29015         }
29016         return Roo.urlDecode(fs);
29017     },
29018     
29019     /**
29020      * Returns the fields in this form as an object with key/value pairs. 
29021      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29022      * @return {Object}
29023      */
29024     getFieldValues : function(with_hidden)
29025     {
29026         if (this.childForms) {
29027             // copy values from the child forms
29028             // should this call getFieldValues - probably not as we do not currently copy
29029             // hidden fields when we generate..
29030             Roo.each(this.childForms, function (f) {
29031                 this.setValues(f.getValues());
29032             }, this);
29033         }
29034         
29035         var ret = {};
29036         this.items.each(function(f){
29037             if (!f.getName()) {
29038                 return;
29039             }
29040             var v = f.getValue();
29041             if (f.inputType =='radio') {
29042                 if (typeof(ret[f.getName()]) == 'undefined') {
29043                     ret[f.getName()] = ''; // empty..
29044                 }
29045                 
29046                 if (!f.el.dom.checked) {
29047                     return;
29048                     
29049                 }
29050                 v = f.el.dom.value;
29051                 
29052             }
29053             
29054             // not sure if this supported any more..
29055             if ((typeof(v) == 'object') && f.getRawValue) {
29056                 v = f.getRawValue() ; // dates..
29057             }
29058             // combo boxes where name != hiddenName...
29059             if (f.name != f.getName()) {
29060                 ret[f.name] = f.getRawValue();
29061             }
29062             ret[f.getName()] = v;
29063         });
29064         
29065         return ret;
29066     },
29067
29068     /**
29069      * Clears all invalid messages in this form.
29070      * @return {BasicForm} this
29071      */
29072     clearInvalid : function(){
29073         this.items.each(function(f){
29074            f.clearInvalid();
29075         });
29076         
29077         Roo.each(this.childForms || [], function (f) {
29078             f.clearInvalid();
29079         });
29080         
29081         
29082         return this;
29083     },
29084
29085     /**
29086      * Resets this form.
29087      * @return {BasicForm} this
29088      */
29089     reset : function(){
29090         this.items.each(function(f){
29091             f.reset();
29092         });
29093         
29094         Roo.each(this.childForms || [], function (f) {
29095             f.reset();
29096         });
29097        
29098         
29099         return this;
29100     },
29101
29102     /**
29103      * Add Roo.form components to this form.
29104      * @param {Field} field1
29105      * @param {Field} field2 (optional)
29106      * @param {Field} etc (optional)
29107      * @return {BasicForm} this
29108      */
29109     add : function(){
29110         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29111         return this;
29112     },
29113
29114
29115     /**
29116      * Removes a field from the items collection (does NOT remove its markup).
29117      * @param {Field} field
29118      * @return {BasicForm} this
29119      */
29120     remove : function(field){
29121         this.items.remove(field);
29122         return this;
29123     },
29124
29125     /**
29126      * Looks at the fields in this form, checks them for an id attribute,
29127      * and calls applyTo on the existing dom element with that id.
29128      * @return {BasicForm} this
29129      */
29130     render : function(){
29131         this.items.each(function(f){
29132             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29133                 f.applyTo(f.id);
29134             }
29135         });
29136         return this;
29137     },
29138
29139     /**
29140      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29141      * @param {Object} values
29142      * @return {BasicForm} this
29143      */
29144     applyToFields : function(o){
29145         this.items.each(function(f){
29146            Roo.apply(f, o);
29147         });
29148         return this;
29149     },
29150
29151     /**
29152      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29153      * @param {Object} values
29154      * @return {BasicForm} this
29155      */
29156     applyIfToFields : function(o){
29157         this.items.each(function(f){
29158            Roo.applyIf(f, o);
29159         });
29160         return this;
29161     }
29162 });
29163
29164 // back compat
29165 Roo.BasicForm = Roo.form.BasicForm;/*
29166  * Based on:
29167  * Ext JS Library 1.1.1
29168  * Copyright(c) 2006-2007, Ext JS, LLC.
29169  *
29170  * Originally Released Under LGPL - original licence link has changed is not relivant.
29171  *
29172  * Fork - LGPL
29173  * <script type="text/javascript">
29174  */
29175
29176 /**
29177  * @class Roo.form.Form
29178  * @extends Roo.form.BasicForm
29179  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29180  * @constructor
29181  * @param {Object} config Configuration options
29182  */
29183 Roo.form.Form = function(config){
29184     var xitems =  [];
29185     if (config.items) {
29186         xitems = config.items;
29187         delete config.items;
29188     }
29189    
29190     
29191     Roo.form.Form.superclass.constructor.call(this, null, config);
29192     this.url = this.url || this.action;
29193     if(!this.root){
29194         this.root = new Roo.form.Layout(Roo.applyIf({
29195             id: Roo.id()
29196         }, config));
29197     }
29198     this.active = this.root;
29199     /**
29200      * Array of all the buttons that have been added to this form via {@link addButton}
29201      * @type Array
29202      */
29203     this.buttons = [];
29204     this.allItems = [];
29205     this.addEvents({
29206         /**
29207          * @event clientvalidation
29208          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29209          * @param {Form} this
29210          * @param {Boolean} valid true if the form has passed client-side validation
29211          */
29212         clientvalidation: true,
29213         /**
29214          * @event rendered
29215          * Fires when the form is rendered
29216          * @param {Roo.form.Form} form
29217          */
29218         rendered : true
29219     });
29220     
29221     if (this.progressUrl) {
29222             // push a hidden field onto the list of fields..
29223             this.addxtype( {
29224                     xns: Roo.form, 
29225                     xtype : 'Hidden', 
29226                     name : 'UPLOAD_IDENTIFIER' 
29227             });
29228         }
29229         
29230     
29231     Roo.each(xitems, this.addxtype, this);
29232     
29233     
29234     
29235 };
29236
29237 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29238     /**
29239      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29240      */
29241     /**
29242      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29243      */
29244     /**
29245      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29246      */
29247     buttonAlign:'center',
29248
29249     /**
29250      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29251      */
29252     minButtonWidth:75,
29253
29254     /**
29255      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29256      * This property cascades to child containers if not set.
29257      */
29258     labelAlign:'left',
29259
29260     /**
29261      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29262      * fires a looping event with that state. This is required to bind buttons to the valid
29263      * state using the config value formBind:true on the button.
29264      */
29265     monitorValid : false,
29266
29267     /**
29268      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29269      */
29270     monitorPoll : 200,
29271     
29272     /**
29273      * @cfg {String} progressUrl - Url to return progress data 
29274      */
29275     
29276     progressUrl : false,
29277   
29278     /**
29279      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29280      * fields are added and the column is closed. If no fields are passed the column remains open
29281      * until end() is called.
29282      * @param {Object} config The config to pass to the column
29283      * @param {Field} field1 (optional)
29284      * @param {Field} field2 (optional)
29285      * @param {Field} etc (optional)
29286      * @return Column The column container object
29287      */
29288     column : function(c){
29289         var col = new Roo.form.Column(c);
29290         this.start(col);
29291         if(arguments.length > 1){ // duplicate code required because of Opera
29292             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29293             this.end();
29294         }
29295         return col;
29296     },
29297
29298     /**
29299      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29300      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29301      * until end() is called.
29302      * @param {Object} config The config to pass to the fieldset
29303      * @param {Field} field1 (optional)
29304      * @param {Field} field2 (optional)
29305      * @param {Field} etc (optional)
29306      * @return FieldSet The fieldset container object
29307      */
29308     fieldset : function(c){
29309         var fs = new Roo.form.FieldSet(c);
29310         this.start(fs);
29311         if(arguments.length > 1){ // duplicate code required because of Opera
29312             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29313             this.end();
29314         }
29315         return fs;
29316     },
29317
29318     /**
29319      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29320      * fields are added and the container is closed. If no fields are passed the container remains open
29321      * until end() is called.
29322      * @param {Object} config The config to pass to the Layout
29323      * @param {Field} field1 (optional)
29324      * @param {Field} field2 (optional)
29325      * @param {Field} etc (optional)
29326      * @return Layout The container object
29327      */
29328     container : function(c){
29329         var l = new Roo.form.Layout(c);
29330         this.start(l);
29331         if(arguments.length > 1){ // duplicate code required because of Opera
29332             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29333             this.end();
29334         }
29335         return l;
29336     },
29337
29338     /**
29339      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29340      * @param {Object} container A Roo.form.Layout or subclass of Layout
29341      * @return {Form} this
29342      */
29343     start : function(c){
29344         // cascade label info
29345         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29346         this.active.stack.push(c);
29347         c.ownerCt = this.active;
29348         this.active = c;
29349         return this;
29350     },
29351
29352     /**
29353      * Closes the current open container
29354      * @return {Form} this
29355      */
29356     end : function(){
29357         if(this.active == this.root){
29358             return this;
29359         }
29360         this.active = this.active.ownerCt;
29361         return this;
29362     },
29363
29364     /**
29365      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29366      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29367      * as the label of the field.
29368      * @param {Field} field1
29369      * @param {Field} field2 (optional)
29370      * @param {Field} etc. (optional)
29371      * @return {Form} this
29372      */
29373     add : function(){
29374         this.active.stack.push.apply(this.active.stack, arguments);
29375         this.allItems.push.apply(this.allItems,arguments);
29376         var r = [];
29377         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29378             if(a[i].isFormField){
29379                 r.push(a[i]);
29380             }
29381         }
29382         if(r.length > 0){
29383             Roo.form.Form.superclass.add.apply(this, r);
29384         }
29385         return this;
29386     },
29387     
29388
29389     
29390     
29391     
29392      /**
29393      * Find any element that has been added to a form, using it's ID or name
29394      * This can include framesets, columns etc. along with regular fields..
29395      * @param {String} id - id or name to find.
29396      
29397      * @return {Element} e - or false if nothing found.
29398      */
29399     findbyId : function(id)
29400     {
29401         var ret = false;
29402         if (!id) {
29403             return ret;
29404         }
29405         Roo.each(this.allItems, function(f){
29406             if (f.id == id || f.name == id ){
29407                 ret = f;
29408                 return false;
29409             }
29410         });
29411         return ret;
29412     },
29413
29414     
29415     
29416     /**
29417      * Render this form into the passed container. This should only be called once!
29418      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29419      * @return {Form} this
29420      */
29421     render : function(ct)
29422     {
29423         
29424         
29425         
29426         ct = Roo.get(ct);
29427         var o = this.autoCreate || {
29428             tag: 'form',
29429             method : this.method || 'POST',
29430             id : this.id || Roo.id()
29431         };
29432         this.initEl(ct.createChild(o));
29433
29434         this.root.render(this.el);
29435         
29436        
29437              
29438         this.items.each(function(f){
29439             f.render('x-form-el-'+f.id);
29440         });
29441
29442         if(this.buttons.length > 0){
29443             // tables are required to maintain order and for correct IE layout
29444             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29445                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29446                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29447             }}, null, true);
29448             var tr = tb.getElementsByTagName('tr')[0];
29449             for(var i = 0, len = this.buttons.length; i < len; i++) {
29450                 var b = this.buttons[i];
29451                 var td = document.createElement('td');
29452                 td.className = 'x-form-btn-td';
29453                 b.render(tr.appendChild(td));
29454             }
29455         }
29456         if(this.monitorValid){ // initialize after render
29457             this.startMonitoring();
29458         }
29459         this.fireEvent('rendered', this);
29460         return this;
29461     },
29462
29463     /**
29464      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29465      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29466      * object or a valid Roo.DomHelper element config
29467      * @param {Function} handler The function called when the button is clicked
29468      * @param {Object} scope (optional) The scope of the handler function
29469      * @return {Roo.Button}
29470      */
29471     addButton : function(config, handler, scope){
29472         var bc = {
29473             handler: handler,
29474             scope: scope,
29475             minWidth: this.minButtonWidth,
29476             hideParent:true
29477         };
29478         if(typeof config == "string"){
29479             bc.text = config;
29480         }else{
29481             Roo.apply(bc, config);
29482         }
29483         var btn = new Roo.Button(null, bc);
29484         this.buttons.push(btn);
29485         return btn;
29486     },
29487
29488      /**
29489      * Adds a series of form elements (using the xtype property as the factory method.
29490      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29491      * @param {Object} config 
29492      */
29493     
29494     addxtype : function()
29495     {
29496         var ar = Array.prototype.slice.call(arguments, 0);
29497         var ret = false;
29498         for(var i = 0; i < ar.length; i++) {
29499             if (!ar[i]) {
29500                 continue; // skip -- if this happends something invalid got sent, we 
29501                 // should ignore it, as basically that interface element will not show up
29502                 // and that should be pretty obvious!!
29503             }
29504             
29505             if (Roo.form[ar[i].xtype]) {
29506                 ar[i].form = this;
29507                 var fe = Roo.factory(ar[i], Roo.form);
29508                 if (!ret) {
29509                     ret = fe;
29510                 }
29511                 fe.form = this;
29512                 if (fe.store) {
29513                     fe.store.form = this;
29514                 }
29515                 if (fe.isLayout) {  
29516                          
29517                     this.start(fe);
29518                     this.allItems.push(fe);
29519                     if (fe.items && fe.addxtype) {
29520                         fe.addxtype.apply(fe, fe.items);
29521                         delete fe.items;
29522                     }
29523                      this.end();
29524                     continue;
29525                 }
29526                 
29527                 
29528                  
29529                 this.add(fe);
29530               //  console.log('adding ' + ar[i].xtype);
29531             }
29532             if (ar[i].xtype == 'Button') {  
29533                 //console.log('adding button');
29534                 //console.log(ar[i]);
29535                 this.addButton(ar[i]);
29536                 this.allItems.push(fe);
29537                 continue;
29538             }
29539             
29540             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29541                 alert('end is not supported on xtype any more, use items');
29542             //    this.end();
29543             //    //console.log('adding end');
29544             }
29545             
29546         }
29547         return ret;
29548     },
29549     
29550     /**
29551      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29552      * option "monitorValid"
29553      */
29554     startMonitoring : function(){
29555         if(!this.bound){
29556             this.bound = true;
29557             Roo.TaskMgr.start({
29558                 run : this.bindHandler,
29559                 interval : this.monitorPoll || 200,
29560                 scope: this
29561             });
29562         }
29563     },
29564
29565     /**
29566      * Stops monitoring of the valid state of this form
29567      */
29568     stopMonitoring : function(){
29569         this.bound = false;
29570     },
29571
29572     // private
29573     bindHandler : function(){
29574         if(!this.bound){
29575             return false; // stops binding
29576         }
29577         var valid = true;
29578         this.items.each(function(f){
29579             if(!f.isValid(true)){
29580                 valid = false;
29581                 return false;
29582             }
29583         });
29584         for(var i = 0, len = this.buttons.length; i < len; i++){
29585             var btn = this.buttons[i];
29586             if(btn.formBind === true && btn.disabled === valid){
29587                 btn.setDisabled(!valid);
29588             }
29589         }
29590         this.fireEvent('clientvalidation', this, valid);
29591     }
29592     
29593     
29594     
29595     
29596     
29597     
29598     
29599     
29600 });
29601
29602
29603 // back compat
29604 Roo.Form = Roo.form.Form;
29605 /*
29606  * Based on:
29607  * Ext JS Library 1.1.1
29608  * Copyright(c) 2006-2007, Ext JS, LLC.
29609  *
29610  * Originally Released Under LGPL - original licence link has changed is not relivant.
29611  *
29612  * Fork - LGPL
29613  * <script type="text/javascript">
29614  */
29615
29616 // as we use this in bootstrap.
29617 Roo.namespace('Roo.form');
29618  /**
29619  * @class Roo.form.Action
29620  * Internal Class used to handle form actions
29621  * @constructor
29622  * @param {Roo.form.BasicForm} el The form element or its id
29623  * @param {Object} config Configuration options
29624  */
29625
29626  
29627  
29628 // define the action interface
29629 Roo.form.Action = function(form, options){
29630     this.form = form;
29631     this.options = options || {};
29632 };
29633 /**
29634  * Client Validation Failed
29635  * @const 
29636  */
29637 Roo.form.Action.CLIENT_INVALID = 'client';
29638 /**
29639  * Server Validation Failed
29640  * @const 
29641  */
29642 Roo.form.Action.SERVER_INVALID = 'server';
29643  /**
29644  * Connect to Server Failed
29645  * @const 
29646  */
29647 Roo.form.Action.CONNECT_FAILURE = 'connect';
29648 /**
29649  * Reading Data from Server Failed
29650  * @const 
29651  */
29652 Roo.form.Action.LOAD_FAILURE = 'load';
29653
29654 Roo.form.Action.prototype = {
29655     type : 'default',
29656     failureType : undefined,
29657     response : undefined,
29658     result : undefined,
29659
29660     // interface method
29661     run : function(options){
29662
29663     },
29664
29665     // interface method
29666     success : function(response){
29667
29668     },
29669
29670     // interface method
29671     handleResponse : function(response){
29672
29673     },
29674
29675     // default connection failure
29676     failure : function(response){
29677         
29678         this.response = response;
29679         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29680         this.form.afterAction(this, false);
29681     },
29682
29683     processResponse : function(response){
29684         this.response = response;
29685         if(!response.responseText){
29686             return true;
29687         }
29688         this.result = this.handleResponse(response);
29689         return this.result;
29690     },
29691
29692     // utility functions used internally
29693     getUrl : function(appendParams){
29694         var url = this.options.url || this.form.url || this.form.el.dom.action;
29695         if(appendParams){
29696             var p = this.getParams();
29697             if(p){
29698                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29699             }
29700         }
29701         return url;
29702     },
29703
29704     getMethod : function(){
29705         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29706     },
29707
29708     getParams : function(){
29709         var bp = this.form.baseParams;
29710         var p = this.options.params;
29711         if(p){
29712             if(typeof p == "object"){
29713                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29714             }else if(typeof p == 'string' && bp){
29715                 p += '&' + Roo.urlEncode(bp);
29716             }
29717         }else if(bp){
29718             p = Roo.urlEncode(bp);
29719         }
29720         return p;
29721     },
29722
29723     createCallback : function(){
29724         return {
29725             success: this.success,
29726             failure: this.failure,
29727             scope: this,
29728             timeout: (this.form.timeout*1000),
29729             upload: this.form.fileUpload ? this.success : undefined
29730         };
29731     }
29732 };
29733
29734 Roo.form.Action.Submit = function(form, options){
29735     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29736 };
29737
29738 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29739     type : 'submit',
29740
29741     haveProgress : false,
29742     uploadComplete : false,
29743     
29744     // uploadProgress indicator.
29745     uploadProgress : function()
29746     {
29747         if (!this.form.progressUrl) {
29748             return;
29749         }
29750         
29751         if (!this.haveProgress) {
29752             Roo.MessageBox.progress("Uploading", "Uploading");
29753         }
29754         if (this.uploadComplete) {
29755            Roo.MessageBox.hide();
29756            return;
29757         }
29758         
29759         this.haveProgress = true;
29760    
29761         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29762         
29763         var c = new Roo.data.Connection();
29764         c.request({
29765             url : this.form.progressUrl,
29766             params: {
29767                 id : uid
29768             },
29769             method: 'GET',
29770             success : function(req){
29771                //console.log(data);
29772                 var rdata = false;
29773                 var edata;
29774                 try  {
29775                    rdata = Roo.decode(req.responseText)
29776                 } catch (e) {
29777                     Roo.log("Invalid data from server..");
29778                     Roo.log(edata);
29779                     return;
29780                 }
29781                 if (!rdata || !rdata.success) {
29782                     Roo.log(rdata);
29783                     Roo.MessageBox.alert(Roo.encode(rdata));
29784                     return;
29785                 }
29786                 var data = rdata.data;
29787                 
29788                 if (this.uploadComplete) {
29789                    Roo.MessageBox.hide();
29790                    return;
29791                 }
29792                    
29793                 if (data){
29794                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29795                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29796                     );
29797                 }
29798                 this.uploadProgress.defer(2000,this);
29799             },
29800        
29801             failure: function(data) {
29802                 Roo.log('progress url failed ');
29803                 Roo.log(data);
29804             },
29805             scope : this
29806         });
29807            
29808     },
29809     
29810     
29811     run : function()
29812     {
29813         // run get Values on the form, so it syncs any secondary forms.
29814         this.form.getValues();
29815         
29816         var o = this.options;
29817         var method = this.getMethod();
29818         var isPost = method == 'POST';
29819         if(o.clientValidation === false || this.form.isValid()){
29820             
29821             if (this.form.progressUrl) {
29822                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29823                     (new Date() * 1) + '' + Math.random());
29824                     
29825             } 
29826             
29827             
29828             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29829                 form:this.form.el.dom,
29830                 url:this.getUrl(!isPost),
29831                 method: method,
29832                 params:isPost ? this.getParams() : null,
29833                 isUpload: this.form.fileUpload
29834             }));
29835             
29836             this.uploadProgress();
29837
29838         }else if (o.clientValidation !== false){ // client validation failed
29839             this.failureType = Roo.form.Action.CLIENT_INVALID;
29840             this.form.afterAction(this, false);
29841         }
29842     },
29843
29844     success : function(response)
29845     {
29846         this.uploadComplete= true;
29847         if (this.haveProgress) {
29848             Roo.MessageBox.hide();
29849         }
29850         
29851         
29852         var result = this.processResponse(response);
29853         if(result === true || result.success){
29854             this.form.afterAction(this, true);
29855             return;
29856         }
29857         if(result.errors){
29858             this.form.markInvalid(result.errors);
29859             this.failureType = Roo.form.Action.SERVER_INVALID;
29860         }
29861         this.form.afterAction(this, false);
29862     },
29863     failure : function(response)
29864     {
29865         this.uploadComplete= true;
29866         if (this.haveProgress) {
29867             Roo.MessageBox.hide();
29868         }
29869         
29870         this.response = response;
29871         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29872         this.form.afterAction(this, false);
29873     },
29874     
29875     handleResponse : function(response){
29876         if(this.form.errorReader){
29877             var rs = this.form.errorReader.read(response);
29878             var errors = [];
29879             if(rs.records){
29880                 for(var i = 0, len = rs.records.length; i < len; i++) {
29881                     var r = rs.records[i];
29882                     errors[i] = r.data;
29883                 }
29884             }
29885             if(errors.length < 1){
29886                 errors = null;
29887             }
29888             return {
29889                 success : rs.success,
29890                 errors : errors
29891             };
29892         }
29893         var ret = false;
29894         try {
29895             ret = Roo.decode(response.responseText);
29896         } catch (e) {
29897             ret = {
29898                 success: false,
29899                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29900                 errors : []
29901             };
29902         }
29903         return ret;
29904         
29905     }
29906 });
29907
29908
29909 Roo.form.Action.Load = function(form, options){
29910     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29911     this.reader = this.form.reader;
29912 };
29913
29914 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29915     type : 'load',
29916
29917     run : function(){
29918         
29919         Roo.Ajax.request(Roo.apply(
29920                 this.createCallback(), {
29921                     method:this.getMethod(),
29922                     url:this.getUrl(false),
29923                     params:this.getParams()
29924         }));
29925     },
29926
29927     success : function(response){
29928         
29929         var result = this.processResponse(response);
29930         if(result === true || !result.success || !result.data){
29931             this.failureType = Roo.form.Action.LOAD_FAILURE;
29932             this.form.afterAction(this, false);
29933             return;
29934         }
29935         this.form.clearInvalid();
29936         this.form.setValues(result.data);
29937         this.form.afterAction(this, true);
29938     },
29939
29940     handleResponse : function(response){
29941         if(this.form.reader){
29942             var rs = this.form.reader.read(response);
29943             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29944             return {
29945                 success : rs.success,
29946                 data : data
29947             };
29948         }
29949         return Roo.decode(response.responseText);
29950     }
29951 });
29952
29953 Roo.form.Action.ACTION_TYPES = {
29954     'load' : Roo.form.Action.Load,
29955     'submit' : Roo.form.Action.Submit
29956 };/*
29957  * Based on:
29958  * Ext JS Library 1.1.1
29959  * Copyright(c) 2006-2007, Ext JS, LLC.
29960  *
29961  * Originally Released Under LGPL - original licence link has changed is not relivant.
29962  *
29963  * Fork - LGPL
29964  * <script type="text/javascript">
29965  */
29966  
29967 /**
29968  * @class Roo.form.Layout
29969  * @extends Roo.Component
29970  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29971  * @constructor
29972  * @param {Object} config Configuration options
29973  */
29974 Roo.form.Layout = function(config){
29975     var xitems = [];
29976     if (config.items) {
29977         xitems = config.items;
29978         delete config.items;
29979     }
29980     Roo.form.Layout.superclass.constructor.call(this, config);
29981     this.stack = [];
29982     Roo.each(xitems, this.addxtype, this);
29983      
29984 };
29985
29986 Roo.extend(Roo.form.Layout, Roo.Component, {
29987     /**
29988      * @cfg {String/Object} autoCreate
29989      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29990      */
29991     /**
29992      * @cfg {String/Object/Function} style
29993      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29994      * a function which returns such a specification.
29995      */
29996     /**
29997      * @cfg {String} labelAlign
29998      * Valid values are "left," "top" and "right" (defaults to "left")
29999      */
30000     /**
30001      * @cfg {Number} labelWidth
30002      * Fixed width in pixels of all field labels (defaults to undefined)
30003      */
30004     /**
30005      * @cfg {Boolean} clear
30006      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30007      */
30008     clear : true,
30009     /**
30010      * @cfg {String} labelSeparator
30011      * The separator to use after field labels (defaults to ':')
30012      */
30013     labelSeparator : ':',
30014     /**
30015      * @cfg {Boolean} hideLabels
30016      * True to suppress the display of field labels in this layout (defaults to false)
30017      */
30018     hideLabels : false,
30019
30020     // private
30021     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30022     
30023     isLayout : true,
30024     
30025     // private
30026     onRender : function(ct, position){
30027         if(this.el){ // from markup
30028             this.el = Roo.get(this.el);
30029         }else {  // generate
30030             var cfg = this.getAutoCreate();
30031             this.el = ct.createChild(cfg, position);
30032         }
30033         if(this.style){
30034             this.el.applyStyles(this.style);
30035         }
30036         if(this.labelAlign){
30037             this.el.addClass('x-form-label-'+this.labelAlign);
30038         }
30039         if(this.hideLabels){
30040             this.labelStyle = "display:none";
30041             this.elementStyle = "padding-left:0;";
30042         }else{
30043             if(typeof this.labelWidth == 'number'){
30044                 this.labelStyle = "width:"+this.labelWidth+"px;";
30045                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30046             }
30047             if(this.labelAlign == 'top'){
30048                 this.labelStyle = "width:auto;";
30049                 this.elementStyle = "padding-left:0;";
30050             }
30051         }
30052         var stack = this.stack;
30053         var slen = stack.length;
30054         if(slen > 0){
30055             if(!this.fieldTpl){
30056                 var t = new Roo.Template(
30057                     '<div class="x-form-item {5}">',
30058                         '<label for="{0}" style="{2}">{1}{4}</label>',
30059                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30060                         '</div>',
30061                     '</div><div class="x-form-clear-left"></div>'
30062                 );
30063                 t.disableFormats = true;
30064                 t.compile();
30065                 Roo.form.Layout.prototype.fieldTpl = t;
30066             }
30067             for(var i = 0; i < slen; i++) {
30068                 if(stack[i].isFormField){
30069                     this.renderField(stack[i]);
30070                 }else{
30071                     this.renderComponent(stack[i]);
30072                 }
30073             }
30074         }
30075         if(this.clear){
30076             this.el.createChild({cls:'x-form-clear'});
30077         }
30078     },
30079
30080     // private
30081     renderField : function(f){
30082         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30083                f.id, //0
30084                f.fieldLabel, //1
30085                f.labelStyle||this.labelStyle||'', //2
30086                this.elementStyle||'', //3
30087                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30088                f.itemCls||this.itemCls||''  //5
30089        ], true).getPrevSibling());
30090     },
30091
30092     // private
30093     renderComponent : function(c){
30094         c.render(c.isLayout ? this.el : this.el.createChild());    
30095     },
30096     /**
30097      * Adds a object form elements (using the xtype property as the factory method.)
30098      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30099      * @param {Object} config 
30100      */
30101     addxtype : function(o)
30102     {
30103         // create the lement.
30104         o.form = this.form;
30105         var fe = Roo.factory(o, Roo.form);
30106         this.form.allItems.push(fe);
30107         this.stack.push(fe);
30108         
30109         if (fe.isFormField) {
30110             this.form.items.add(fe);
30111         }
30112          
30113         return fe;
30114     }
30115 });
30116
30117 /**
30118  * @class Roo.form.Column
30119  * @extends Roo.form.Layout
30120  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30121  * @constructor
30122  * @param {Object} config Configuration options
30123  */
30124 Roo.form.Column = function(config){
30125     Roo.form.Column.superclass.constructor.call(this, config);
30126 };
30127
30128 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30129     /**
30130      * @cfg {Number/String} width
30131      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30132      */
30133     /**
30134      * @cfg {String/Object} autoCreate
30135      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30136      */
30137
30138     // private
30139     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30140
30141     // private
30142     onRender : function(ct, position){
30143         Roo.form.Column.superclass.onRender.call(this, ct, position);
30144         if(this.width){
30145             this.el.setWidth(this.width);
30146         }
30147     }
30148 });
30149
30150
30151 /**
30152  * @class Roo.form.Row
30153  * @extends Roo.form.Layout
30154  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30155  * @constructor
30156  * @param {Object} config Configuration options
30157  */
30158
30159  
30160 Roo.form.Row = function(config){
30161     Roo.form.Row.superclass.constructor.call(this, config);
30162 };
30163  
30164 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30165       /**
30166      * @cfg {Number/String} width
30167      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30168      */
30169     /**
30170      * @cfg {Number/String} height
30171      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30172      */
30173     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30174     
30175     padWidth : 20,
30176     // private
30177     onRender : function(ct, position){
30178         //console.log('row render');
30179         if(!this.rowTpl){
30180             var t = new Roo.Template(
30181                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30182                     '<label for="{0}" style="{2}">{1}{4}</label>',
30183                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30184                     '</div>',
30185                 '</div>'
30186             );
30187             t.disableFormats = true;
30188             t.compile();
30189             Roo.form.Layout.prototype.rowTpl = t;
30190         }
30191         this.fieldTpl = this.rowTpl;
30192         
30193         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30194         var labelWidth = 100;
30195         
30196         if ((this.labelAlign != 'top')) {
30197             if (typeof this.labelWidth == 'number') {
30198                 labelWidth = this.labelWidth
30199             }
30200             this.padWidth =  20 + labelWidth;
30201             
30202         }
30203         
30204         Roo.form.Column.superclass.onRender.call(this, ct, position);
30205         if(this.width){
30206             this.el.setWidth(this.width);
30207         }
30208         if(this.height){
30209             this.el.setHeight(this.height);
30210         }
30211     },
30212     
30213     // private
30214     renderField : function(f){
30215         f.fieldEl = this.fieldTpl.append(this.el, [
30216                f.id, f.fieldLabel,
30217                f.labelStyle||this.labelStyle||'',
30218                this.elementStyle||'',
30219                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30220                f.itemCls||this.itemCls||'',
30221                f.width ? f.width + this.padWidth : 160 + this.padWidth
30222        ],true);
30223     }
30224 });
30225  
30226
30227 /**
30228  * @class Roo.form.FieldSet
30229  * @extends Roo.form.Layout
30230  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30231  * @constructor
30232  * @param {Object} config Configuration options
30233  */
30234 Roo.form.FieldSet = function(config){
30235     Roo.form.FieldSet.superclass.constructor.call(this, config);
30236 };
30237
30238 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30239     /**
30240      * @cfg {String} legend
30241      * The text to display as the legend for the FieldSet (defaults to '')
30242      */
30243     /**
30244      * @cfg {String/Object} autoCreate
30245      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30246      */
30247
30248     // private
30249     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30250
30251     // private
30252     onRender : function(ct, position){
30253         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30254         if(this.legend){
30255             this.setLegend(this.legend);
30256         }
30257     },
30258
30259     // private
30260     setLegend : function(text){
30261         if(this.rendered){
30262             this.el.child('legend').update(text);
30263         }
30264     }
30265 });/*
30266  * Based on:
30267  * Ext JS Library 1.1.1
30268  * Copyright(c) 2006-2007, Ext JS, LLC.
30269  *
30270  * Originally Released Under LGPL - original licence link has changed is not relivant.
30271  *
30272  * Fork - LGPL
30273  * <script type="text/javascript">
30274  */
30275 /**
30276  * @class Roo.form.VTypes
30277  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30278  * @singleton
30279  */
30280 Roo.form.VTypes = function(){
30281     // closure these in so they are only created once.
30282     var alpha = /^[a-zA-Z_]+$/;
30283     var alphanum = /^[a-zA-Z0-9_]+$/;
30284     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30285     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30286
30287     // All these messages and functions are configurable
30288     return {
30289         /**
30290          * The function used to validate email addresses
30291          * @param {String} value The email address
30292          */
30293         'email' : function(v){
30294             return email.test(v);
30295         },
30296         /**
30297          * The error text to display when the email validation function returns false
30298          * @type String
30299          */
30300         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30301         /**
30302          * The keystroke filter mask to be applied on email input
30303          * @type RegExp
30304          */
30305         'emailMask' : /[a-z0-9_\.\-@]/i,
30306
30307         /**
30308          * The function used to validate URLs
30309          * @param {String} value The URL
30310          */
30311         'url' : function(v){
30312             return url.test(v);
30313         },
30314         /**
30315          * The error text to display when the url validation function returns false
30316          * @type String
30317          */
30318         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30319         
30320         /**
30321          * The function used to validate alpha values
30322          * @param {String} value The value
30323          */
30324         'alpha' : function(v){
30325             return alpha.test(v);
30326         },
30327         /**
30328          * The error text to display when the alpha validation function returns false
30329          * @type String
30330          */
30331         'alphaText' : 'This field should only contain letters and _',
30332         /**
30333          * The keystroke filter mask to be applied on alpha input
30334          * @type RegExp
30335          */
30336         'alphaMask' : /[a-z_]/i,
30337
30338         /**
30339          * The function used to validate alphanumeric values
30340          * @param {String} value The value
30341          */
30342         'alphanum' : function(v){
30343             return alphanum.test(v);
30344         },
30345         /**
30346          * The error text to display when the alphanumeric validation function returns false
30347          * @type String
30348          */
30349         'alphanumText' : 'This field should only contain letters, numbers and _',
30350         /**
30351          * The keystroke filter mask to be applied on alphanumeric input
30352          * @type RegExp
30353          */
30354         'alphanumMask' : /[a-z0-9_]/i
30355     };
30356 }();//<script type="text/javascript">
30357
30358 /**
30359  * @class Roo.form.FCKeditor
30360  * @extends Roo.form.TextArea
30361  * Wrapper around the FCKEditor http://www.fckeditor.net
30362  * @constructor
30363  * Creates a new FCKeditor
30364  * @param {Object} config Configuration options
30365  */
30366 Roo.form.FCKeditor = function(config){
30367     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30368     this.addEvents({
30369          /**
30370          * @event editorinit
30371          * Fired when the editor is initialized - you can add extra handlers here..
30372          * @param {FCKeditor} this
30373          * @param {Object} the FCK object.
30374          */
30375         editorinit : true
30376     });
30377     
30378     
30379 };
30380 Roo.form.FCKeditor.editors = { };
30381 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30382 {
30383     //defaultAutoCreate : {
30384     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30385     //},
30386     // private
30387     /**
30388      * @cfg {Object} fck options - see fck manual for details.
30389      */
30390     fckconfig : false,
30391     
30392     /**
30393      * @cfg {Object} fck toolbar set (Basic or Default)
30394      */
30395     toolbarSet : 'Basic',
30396     /**
30397      * @cfg {Object} fck BasePath
30398      */ 
30399     basePath : '/fckeditor/',
30400     
30401     
30402     frame : false,
30403     
30404     value : '',
30405     
30406    
30407     onRender : function(ct, position)
30408     {
30409         if(!this.el){
30410             this.defaultAutoCreate = {
30411                 tag: "textarea",
30412                 style:"width:300px;height:60px;",
30413                 autocomplete: "new-password"
30414             };
30415         }
30416         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30417         /*
30418         if(this.grow){
30419             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30420             if(this.preventScrollbars){
30421                 this.el.setStyle("overflow", "hidden");
30422             }
30423             this.el.setHeight(this.growMin);
30424         }
30425         */
30426         //console.log('onrender' + this.getId() );
30427         Roo.form.FCKeditor.editors[this.getId()] = this;
30428          
30429
30430         this.replaceTextarea() ;
30431         
30432     },
30433     
30434     getEditor : function() {
30435         return this.fckEditor;
30436     },
30437     /**
30438      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30439      * @param {Mixed} value The value to set
30440      */
30441     
30442     
30443     setValue : function(value)
30444     {
30445         //console.log('setValue: ' + value);
30446         
30447         if(typeof(value) == 'undefined') { // not sure why this is happending...
30448             return;
30449         }
30450         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30451         
30452         //if(!this.el || !this.getEditor()) {
30453         //    this.value = value;
30454             //this.setValue.defer(100,this,[value]);    
30455         //    return;
30456         //} 
30457         
30458         if(!this.getEditor()) {
30459             return;
30460         }
30461         
30462         this.getEditor().SetData(value);
30463         
30464         //
30465
30466     },
30467
30468     /**
30469      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30470      * @return {Mixed} value The field value
30471      */
30472     getValue : function()
30473     {
30474         
30475         if (this.frame && this.frame.dom.style.display == 'none') {
30476             return Roo.form.FCKeditor.superclass.getValue.call(this);
30477         }
30478         
30479         if(!this.el || !this.getEditor()) {
30480            
30481            // this.getValue.defer(100,this); 
30482             return this.value;
30483         }
30484        
30485         
30486         var value=this.getEditor().GetData();
30487         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30488         return Roo.form.FCKeditor.superclass.getValue.call(this);
30489         
30490
30491     },
30492
30493     /**
30494      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30495      * @return {Mixed} value The field value
30496      */
30497     getRawValue : function()
30498     {
30499         if (this.frame && this.frame.dom.style.display == 'none') {
30500             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30501         }
30502         
30503         if(!this.el || !this.getEditor()) {
30504             //this.getRawValue.defer(100,this); 
30505             return this.value;
30506             return;
30507         }
30508         
30509         
30510         
30511         var value=this.getEditor().GetData();
30512         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30513         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30514          
30515     },
30516     
30517     setSize : function(w,h) {
30518         
30519         
30520         
30521         //if (this.frame && this.frame.dom.style.display == 'none') {
30522         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30523         //    return;
30524         //}
30525         //if(!this.el || !this.getEditor()) {
30526         //    this.setSize.defer(100,this, [w,h]); 
30527         //    return;
30528         //}
30529         
30530         
30531         
30532         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30533         
30534         this.frame.dom.setAttribute('width', w);
30535         this.frame.dom.setAttribute('height', h);
30536         this.frame.setSize(w,h);
30537         
30538     },
30539     
30540     toggleSourceEdit : function(value) {
30541         
30542       
30543          
30544         this.el.dom.style.display = value ? '' : 'none';
30545         this.frame.dom.style.display = value ?  'none' : '';
30546         
30547     },
30548     
30549     
30550     focus: function(tag)
30551     {
30552         if (this.frame.dom.style.display == 'none') {
30553             return Roo.form.FCKeditor.superclass.focus.call(this);
30554         }
30555         if(!this.el || !this.getEditor()) {
30556             this.focus.defer(100,this, [tag]); 
30557             return;
30558         }
30559         
30560         
30561         
30562         
30563         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30564         this.getEditor().Focus();
30565         if (tgs.length) {
30566             if (!this.getEditor().Selection.GetSelection()) {
30567                 this.focus.defer(100,this, [tag]); 
30568                 return;
30569             }
30570             
30571             
30572             var r = this.getEditor().EditorDocument.createRange();
30573             r.setStart(tgs[0],0);
30574             r.setEnd(tgs[0],0);
30575             this.getEditor().Selection.GetSelection().removeAllRanges();
30576             this.getEditor().Selection.GetSelection().addRange(r);
30577             this.getEditor().Focus();
30578         }
30579         
30580     },
30581     
30582     
30583     
30584     replaceTextarea : function()
30585     {
30586         if ( document.getElementById( this.getId() + '___Frame' ) ) {
30587             return ;
30588         }
30589         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30590         //{
30591             // We must check the elements firstly using the Id and then the name.
30592         var oTextarea = document.getElementById( this.getId() );
30593         
30594         var colElementsByName = document.getElementsByName( this.getId() ) ;
30595          
30596         oTextarea.style.display = 'none' ;
30597
30598         if ( oTextarea.tabIndex ) {            
30599             this.TabIndex = oTextarea.tabIndex ;
30600         }
30601         
30602         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30603         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30604         this.frame = Roo.get(this.getId() + '___Frame')
30605     },
30606     
30607     _getConfigHtml : function()
30608     {
30609         var sConfig = '' ;
30610
30611         for ( var o in this.fckconfig ) {
30612             sConfig += sConfig.length > 0  ? '&amp;' : '';
30613             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30614         }
30615
30616         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30617     },
30618     
30619     
30620     _getIFrameHtml : function()
30621     {
30622         var sFile = 'fckeditor.html' ;
30623         /* no idea what this is about..
30624         try
30625         {
30626             if ( (/fcksource=true/i).test( window.top.location.search ) )
30627                 sFile = 'fckeditor.original.html' ;
30628         }
30629         catch (e) { 
30630         */
30631
30632         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30633         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30634         
30635         
30636         var html = '<iframe id="' + this.getId() +
30637             '___Frame" src="' + sLink +
30638             '" width="' + this.width +
30639             '" height="' + this.height + '"' +
30640             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30641             ' frameborder="0" scrolling="no"></iframe>' ;
30642
30643         return html ;
30644     },
30645     
30646     _insertHtmlBefore : function( html, element )
30647     {
30648         if ( element.insertAdjacentHTML )       {
30649             // IE
30650             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30651         } else { // Gecko
30652             var oRange = document.createRange() ;
30653             oRange.setStartBefore( element ) ;
30654             var oFragment = oRange.createContextualFragment( html );
30655             element.parentNode.insertBefore( oFragment, element ) ;
30656         }
30657     }
30658     
30659     
30660   
30661     
30662     
30663     
30664     
30665
30666 });
30667
30668 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30669
30670 function FCKeditor_OnComplete(editorInstance){
30671     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30672     f.fckEditor = editorInstance;
30673     //console.log("loaded");
30674     f.fireEvent('editorinit', f, editorInstance);
30675
30676   
30677
30678  
30679
30680
30681
30682
30683
30684
30685
30686
30687
30688
30689
30690
30691
30692
30693
30694 //<script type="text/javascript">
30695 /**
30696  * @class Roo.form.GridField
30697  * @extends Roo.form.Field
30698  * Embed a grid (or editable grid into a form)
30699  * STATUS ALPHA
30700  * 
30701  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30702  * it needs 
30703  * xgrid.store = Roo.data.Store
30704  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30705  * xgrid.store.reader = Roo.data.JsonReader 
30706  * 
30707  * 
30708  * @constructor
30709  * Creates a new GridField
30710  * @param {Object} config Configuration options
30711  */
30712 Roo.form.GridField = function(config){
30713     Roo.form.GridField.superclass.constructor.call(this, config);
30714      
30715 };
30716
30717 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30718     /**
30719      * @cfg {Number} width  - used to restrict width of grid..
30720      */
30721     width : 100,
30722     /**
30723      * @cfg {Number} height - used to restrict height of grid..
30724      */
30725     height : 50,
30726      /**
30727      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30728          * 
30729          *}
30730      */
30731     xgrid : false, 
30732     /**
30733      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30734      * {tag: "input", type: "checkbox", autocomplete: "off"})
30735      */
30736    // defaultAutoCreate : { tag: 'div' },
30737     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30738     /**
30739      * @cfg {String} addTitle Text to include for adding a title.
30740      */
30741     addTitle : false,
30742     //
30743     onResize : function(){
30744         Roo.form.Field.superclass.onResize.apply(this, arguments);
30745     },
30746
30747     initEvents : function(){
30748         // Roo.form.Checkbox.superclass.initEvents.call(this);
30749         // has no events...
30750        
30751     },
30752
30753
30754     getResizeEl : function(){
30755         return this.wrap;
30756     },
30757
30758     getPositionEl : function(){
30759         return this.wrap;
30760     },
30761
30762     // private
30763     onRender : function(ct, position){
30764         
30765         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30766         var style = this.style;
30767         delete this.style;
30768         
30769         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30770         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30771         this.viewEl = this.wrap.createChild({ tag: 'div' });
30772         if (style) {
30773             this.viewEl.applyStyles(style);
30774         }
30775         if (this.width) {
30776             this.viewEl.setWidth(this.width);
30777         }
30778         if (this.height) {
30779             this.viewEl.setHeight(this.height);
30780         }
30781         //if(this.inputValue !== undefined){
30782         //this.setValue(this.value);
30783         
30784         
30785         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30786         
30787         
30788         this.grid.render();
30789         this.grid.getDataSource().on('remove', this.refreshValue, this);
30790         this.grid.getDataSource().on('update', this.refreshValue, this);
30791         this.grid.on('afteredit', this.refreshValue, this);
30792  
30793     },
30794      
30795     
30796     /**
30797      * Sets the value of the item. 
30798      * @param {String} either an object  or a string..
30799      */
30800     setValue : function(v){
30801         //this.value = v;
30802         v = v || []; // empty set..
30803         // this does not seem smart - it really only affects memoryproxy grids..
30804         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30805             var ds = this.grid.getDataSource();
30806             // assumes a json reader..
30807             var data = {}
30808             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30809             ds.loadData( data);
30810         }
30811         // clear selection so it does not get stale.
30812         if (this.grid.sm) { 
30813             this.grid.sm.clearSelections();
30814         }
30815         
30816         Roo.form.GridField.superclass.setValue.call(this, v);
30817         this.refreshValue();
30818         // should load data in the grid really....
30819     },
30820     
30821     // private
30822     refreshValue: function() {
30823          var val = [];
30824         this.grid.getDataSource().each(function(r) {
30825             val.push(r.data);
30826         });
30827         this.el.dom.value = Roo.encode(val);
30828     }
30829     
30830      
30831     
30832     
30833 });/*
30834  * Based on:
30835  * Ext JS Library 1.1.1
30836  * Copyright(c) 2006-2007, Ext JS, LLC.
30837  *
30838  * Originally Released Under LGPL - original licence link has changed is not relivant.
30839  *
30840  * Fork - LGPL
30841  * <script type="text/javascript">
30842  */
30843 /**
30844  * @class Roo.form.DisplayField
30845  * @extends Roo.form.Field
30846  * A generic Field to display non-editable data.
30847  * @cfg {Boolean} closable (true|false) default false
30848  * @constructor
30849  * Creates a new Display Field item.
30850  * @param {Object} config Configuration options
30851  */
30852 Roo.form.DisplayField = function(config){
30853     Roo.form.DisplayField.superclass.constructor.call(this, config);
30854     
30855     this.addEvents({
30856         /**
30857          * @event close
30858          * Fires after the click the close btn
30859              * @param {Roo.form.DisplayField} this
30860              */
30861         close : true
30862     });
30863 };
30864
30865 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30866     inputType:      'hidden',
30867     allowBlank:     true,
30868     readOnly:         true,
30869     
30870  
30871     /**
30872      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30873      */
30874     focusClass : undefined,
30875     /**
30876      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30877      */
30878     fieldClass: 'x-form-field',
30879     
30880      /**
30881      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30882      */
30883     valueRenderer: undefined,
30884     
30885     width: 100,
30886     /**
30887      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30888      * {tag: "input", type: "checkbox", autocomplete: "off"})
30889      */
30890      
30891  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30892  
30893     closable : false,
30894     
30895     onResize : function(){
30896         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30897         
30898     },
30899
30900     initEvents : function(){
30901         // Roo.form.Checkbox.superclass.initEvents.call(this);
30902         // has no events...
30903         
30904         if(this.closable){
30905             this.closeEl.on('click', this.onClose, this);
30906         }
30907        
30908     },
30909
30910
30911     getResizeEl : function(){
30912         return this.wrap;
30913     },
30914
30915     getPositionEl : function(){
30916         return this.wrap;
30917     },
30918
30919     // private
30920     onRender : function(ct, position){
30921         
30922         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30923         //if(this.inputValue !== undefined){
30924         this.wrap = this.el.wrap();
30925         
30926         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30927         
30928         if(this.closable){
30929             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
30930         }
30931         
30932         if (this.bodyStyle) {
30933             this.viewEl.applyStyles(this.bodyStyle);
30934         }
30935         //this.viewEl.setStyle('padding', '2px');
30936         
30937         this.setValue(this.value);
30938         
30939     },
30940 /*
30941     // private
30942     initValue : Roo.emptyFn,
30943
30944   */
30945
30946         // private
30947     onClick : function(){
30948         
30949     },
30950
30951     /**
30952      * Sets the checked state of the checkbox.
30953      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30954      */
30955     setValue : function(v){
30956         this.value = v;
30957         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30958         // this might be called before we have a dom element..
30959         if (!this.viewEl) {
30960             return;
30961         }
30962         this.viewEl.dom.innerHTML = html;
30963         Roo.form.DisplayField.superclass.setValue.call(this, v);
30964
30965     },
30966     
30967     onClose : function(e)
30968     {
30969         e.preventDefault();
30970         
30971         this.fireEvent('close', this);
30972     }
30973 });/*
30974  * 
30975  * Licence- LGPL
30976  * 
30977  */
30978
30979 /**
30980  * @class Roo.form.DayPicker
30981  * @extends Roo.form.Field
30982  * A Day picker show [M] [T] [W] ....
30983  * @constructor
30984  * Creates a new Day Picker
30985  * @param {Object} config Configuration options
30986  */
30987 Roo.form.DayPicker= function(config){
30988     Roo.form.DayPicker.superclass.constructor.call(this, config);
30989      
30990 };
30991
30992 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30993     /**
30994      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30995      */
30996     focusClass : undefined,
30997     /**
30998      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30999      */
31000     fieldClass: "x-form-field",
31001    
31002     /**
31003      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31004      * {tag: "input", type: "checkbox", autocomplete: "off"})
31005      */
31006     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31007     
31008    
31009     actionMode : 'viewEl', 
31010     //
31011     // private
31012  
31013     inputType : 'hidden',
31014     
31015      
31016     inputElement: false, // real input element?
31017     basedOn: false, // ????
31018     
31019     isFormField: true, // not sure where this is needed!!!!
31020
31021     onResize : function(){
31022         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31023         if(!this.boxLabel){
31024             this.el.alignTo(this.wrap, 'c-c');
31025         }
31026     },
31027
31028     initEvents : function(){
31029         Roo.form.Checkbox.superclass.initEvents.call(this);
31030         this.el.on("click", this.onClick,  this);
31031         this.el.on("change", this.onClick,  this);
31032     },
31033
31034
31035     getResizeEl : function(){
31036         return this.wrap;
31037     },
31038
31039     getPositionEl : function(){
31040         return this.wrap;
31041     },
31042
31043     
31044     // private
31045     onRender : function(ct, position){
31046         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31047        
31048         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31049         
31050         var r1 = '<table><tr>';
31051         var r2 = '<tr class="x-form-daypick-icons">';
31052         for (var i=0; i < 7; i++) {
31053             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31054             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31055         }
31056         
31057         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31058         viewEl.select('img').on('click', this.onClick, this);
31059         this.viewEl = viewEl;   
31060         
31061         
31062         // this will not work on Chrome!!!
31063         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31064         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31065         
31066         
31067           
31068
31069     },
31070
31071     // private
31072     initValue : Roo.emptyFn,
31073
31074     /**
31075      * Returns the checked state of the checkbox.
31076      * @return {Boolean} True if checked, else false
31077      */
31078     getValue : function(){
31079         return this.el.dom.value;
31080         
31081     },
31082
31083         // private
31084     onClick : function(e){ 
31085         //this.setChecked(!this.checked);
31086         Roo.get(e.target).toggleClass('x-menu-item-checked');
31087         this.refreshValue();
31088         //if(this.el.dom.checked != this.checked){
31089         //    this.setValue(this.el.dom.checked);
31090        // }
31091     },
31092     
31093     // private
31094     refreshValue : function()
31095     {
31096         var val = '';
31097         this.viewEl.select('img',true).each(function(e,i,n)  {
31098             val += e.is(".x-menu-item-checked") ? String(n) : '';
31099         });
31100         this.setValue(val, true);
31101     },
31102
31103     /**
31104      * Sets the checked state of the checkbox.
31105      * On is always based on a string comparison between inputValue and the param.
31106      * @param {Boolean/String} value - the value to set 
31107      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31108      */
31109     setValue : function(v,suppressEvent){
31110         if (!this.el.dom) {
31111             return;
31112         }
31113         var old = this.el.dom.value ;
31114         this.el.dom.value = v;
31115         if (suppressEvent) {
31116             return ;
31117         }
31118          
31119         // update display..
31120         this.viewEl.select('img',true).each(function(e,i,n)  {
31121             
31122             var on = e.is(".x-menu-item-checked");
31123             var newv = v.indexOf(String(n)) > -1;
31124             if (on != newv) {
31125                 e.toggleClass('x-menu-item-checked');
31126             }
31127             
31128         });
31129         
31130         
31131         this.fireEvent('change', this, v, old);
31132         
31133         
31134     },
31135    
31136     // handle setting of hidden value by some other method!!?!?
31137     setFromHidden: function()
31138     {
31139         if(!this.el){
31140             return;
31141         }
31142         //console.log("SET FROM HIDDEN");
31143         //alert('setFrom hidden');
31144         this.setValue(this.el.dom.value);
31145     },
31146     
31147     onDestroy : function()
31148     {
31149         if(this.viewEl){
31150             Roo.get(this.viewEl).remove();
31151         }
31152          
31153         Roo.form.DayPicker.superclass.onDestroy.call(this);
31154     }
31155
31156 });/*
31157  * RooJS Library 1.1.1
31158  * Copyright(c) 2008-2011  Alan Knowles
31159  *
31160  * License - LGPL
31161  */
31162  
31163
31164 /**
31165  * @class Roo.form.ComboCheck
31166  * @extends Roo.form.ComboBox
31167  * A combobox for multiple select items.
31168  *
31169  * FIXME - could do with a reset button..
31170  * 
31171  * @constructor
31172  * Create a new ComboCheck
31173  * @param {Object} config Configuration options
31174  */
31175 Roo.form.ComboCheck = function(config){
31176     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31177     // should verify some data...
31178     // like
31179     // hiddenName = required..
31180     // displayField = required
31181     // valudField == required
31182     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31183     var _t = this;
31184     Roo.each(req, function(e) {
31185         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31186             throw "Roo.form.ComboCheck : missing value for: " + e;
31187         }
31188     });
31189     
31190     
31191 };
31192
31193 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31194      
31195      
31196     editable : false,
31197      
31198     selectedClass: 'x-menu-item-checked', 
31199     
31200     // private
31201     onRender : function(ct, position){
31202         var _t = this;
31203         
31204         
31205         
31206         if(!this.tpl){
31207             var cls = 'x-combo-list';
31208
31209             
31210             this.tpl =  new Roo.Template({
31211                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31212                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31213                    '<span>{' + this.displayField + '}</span>' +
31214                     '</div>' 
31215                 
31216             });
31217         }
31218  
31219         
31220         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31221         this.view.singleSelect = false;
31222         this.view.multiSelect = true;
31223         this.view.toggleSelect = true;
31224         this.pageTb.add(new Roo.Toolbar.Fill(), {
31225             
31226             text: 'Done',
31227             handler: function()
31228             {
31229                 _t.collapse();
31230             }
31231         });
31232     },
31233     
31234     onViewOver : function(e, t){
31235         // do nothing...
31236         return;
31237         
31238     },
31239     
31240     onViewClick : function(doFocus,index){
31241         return;
31242         
31243     },
31244     select: function () {
31245         //Roo.log("SELECT CALLED");
31246     },
31247      
31248     selectByValue : function(xv, scrollIntoView){
31249         var ar = this.getValueArray();
31250         var sels = [];
31251         
31252         Roo.each(ar, function(v) {
31253             if(v === undefined || v === null){
31254                 return;
31255             }
31256             var r = this.findRecord(this.valueField, v);
31257             if(r){
31258                 sels.push(this.store.indexOf(r))
31259                 
31260             }
31261         },this);
31262         this.view.select(sels);
31263         return false;
31264     },
31265     
31266     
31267     
31268     onSelect : function(record, index){
31269        // Roo.log("onselect Called");
31270        // this is only called by the clear button now..
31271         this.view.clearSelections();
31272         this.setValue('[]');
31273         if (this.value != this.valueBefore) {
31274             this.fireEvent('change', this, this.value, this.valueBefore);
31275             this.valueBefore = this.value;
31276         }
31277     },
31278     getValueArray : function()
31279     {
31280         var ar = [] ;
31281         
31282         try {
31283             //Roo.log(this.value);
31284             if (typeof(this.value) == 'undefined') {
31285                 return [];
31286             }
31287             var ar = Roo.decode(this.value);
31288             return  ar instanceof Array ? ar : []; //?? valid?
31289             
31290         } catch(e) {
31291             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31292             return [];
31293         }
31294          
31295     },
31296     expand : function ()
31297     {
31298         
31299         Roo.form.ComboCheck.superclass.expand.call(this);
31300         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31301         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31302         
31303
31304     },
31305     
31306     collapse : function(){
31307         Roo.form.ComboCheck.superclass.collapse.call(this);
31308         var sl = this.view.getSelectedIndexes();
31309         var st = this.store;
31310         var nv = [];
31311         var tv = [];
31312         var r;
31313         Roo.each(sl, function(i) {
31314             r = st.getAt(i);
31315             nv.push(r.get(this.valueField));
31316         },this);
31317         this.setValue(Roo.encode(nv));
31318         if (this.value != this.valueBefore) {
31319
31320             this.fireEvent('change', this, this.value, this.valueBefore);
31321             this.valueBefore = this.value;
31322         }
31323         
31324     },
31325     
31326     setValue : function(v){
31327         // Roo.log(v);
31328         this.value = v;
31329         
31330         var vals = this.getValueArray();
31331         var tv = [];
31332         Roo.each(vals, function(k) {
31333             var r = this.findRecord(this.valueField, k);
31334             if(r){
31335                 tv.push(r.data[this.displayField]);
31336             }else if(this.valueNotFoundText !== undefined){
31337                 tv.push( this.valueNotFoundText );
31338             }
31339         },this);
31340        // Roo.log(tv);
31341         
31342         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31343         this.hiddenField.value = v;
31344         this.value = v;
31345     }
31346     
31347 });/*
31348  * Based on:
31349  * Ext JS Library 1.1.1
31350  * Copyright(c) 2006-2007, Ext JS, LLC.
31351  *
31352  * Originally Released Under LGPL - original licence link has changed is not relivant.
31353  *
31354  * Fork - LGPL
31355  * <script type="text/javascript">
31356  */
31357  
31358 /**
31359  * @class Roo.form.Signature
31360  * @extends Roo.form.Field
31361  * Signature field.  
31362  * @constructor
31363  * 
31364  * @param {Object} config Configuration options
31365  */
31366
31367 Roo.form.Signature = function(config){
31368     Roo.form.Signature.superclass.constructor.call(this, config);
31369     
31370     this.addEvents({// not in used??
31371          /**
31372          * @event confirm
31373          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31374              * @param {Roo.form.Signature} combo This combo box
31375              */
31376         'confirm' : true,
31377         /**
31378          * @event reset
31379          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31380              * @param {Roo.form.ComboBox} combo This combo box
31381              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31382              */
31383         'reset' : true
31384     });
31385 };
31386
31387 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31388     /**
31389      * @cfg {Object} labels Label to use when rendering a form.
31390      * defaults to 
31391      * labels : { 
31392      *      clear : "Clear",
31393      *      confirm : "Confirm"
31394      *  }
31395      */
31396     labels : { 
31397         clear : "Clear",
31398         confirm : "Confirm"
31399     },
31400     /**
31401      * @cfg {Number} width The signature panel width (defaults to 300)
31402      */
31403     width: 300,
31404     /**
31405      * @cfg {Number} height The signature panel height (defaults to 100)
31406      */
31407     height : 100,
31408     /**
31409      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31410      */
31411     allowBlank : false,
31412     
31413     //private
31414     // {Object} signPanel The signature SVG panel element (defaults to {})
31415     signPanel : {},
31416     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31417     isMouseDown : false,
31418     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31419     isConfirmed : false,
31420     // {String} signatureTmp SVG mapping string (defaults to empty string)
31421     signatureTmp : '',
31422     
31423     
31424     defaultAutoCreate : { // modified by initCompnoent..
31425         tag: "input",
31426         type:"hidden"
31427     },
31428
31429     // private
31430     onRender : function(ct, position){
31431         
31432         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31433         
31434         this.wrap = this.el.wrap({
31435             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31436         });
31437         
31438         this.createToolbar(this);
31439         this.signPanel = this.wrap.createChild({
31440                 tag: 'div',
31441                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31442             }, this.el
31443         );
31444             
31445         this.svgID = Roo.id();
31446         this.svgEl = this.signPanel.createChild({
31447               xmlns : 'http://www.w3.org/2000/svg',
31448               tag : 'svg',
31449               id : this.svgID + "-svg",
31450               width: this.width,
31451               height: this.height,
31452               viewBox: '0 0 '+this.width+' '+this.height,
31453               cn : [
31454                 {
31455                     tag: "rect",
31456                     id: this.svgID + "-svg-r",
31457                     width: this.width,
31458                     height: this.height,
31459                     fill: "#ffa"
31460                 },
31461                 {
31462                     tag: "line",
31463                     id: this.svgID + "-svg-l",
31464                     x1: "0", // start
31465                     y1: (this.height*0.8), // start set the line in 80% of height
31466                     x2: this.width, // end
31467                     y2: (this.height*0.8), // end set the line in 80% of height
31468                     'stroke': "#666",
31469                     'stroke-width': "1",
31470                     'stroke-dasharray': "3",
31471                     'shape-rendering': "crispEdges",
31472                     'pointer-events': "none"
31473                 },
31474                 {
31475                     tag: "path",
31476                     id: this.svgID + "-svg-p",
31477                     'stroke': "navy",
31478                     'stroke-width': "3",
31479                     'fill': "none",
31480                     'pointer-events': 'none'
31481                 }
31482               ]
31483         });
31484         this.createSVG();
31485         this.svgBox = this.svgEl.dom.getScreenCTM();
31486     },
31487     createSVG : function(){ 
31488         var svg = this.signPanel;
31489         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31490         var t = this;
31491
31492         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31493         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31494         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31495         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31496         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31497         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31498         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31499         
31500     },
31501     isTouchEvent : function(e){
31502         return e.type.match(/^touch/);
31503     },
31504     getCoords : function (e) {
31505         var pt    = this.svgEl.dom.createSVGPoint();
31506         pt.x = e.clientX; 
31507         pt.y = e.clientY;
31508         if (this.isTouchEvent(e)) {
31509             pt.x =  e.targetTouches[0].clientX;
31510             pt.y = e.targetTouches[0].clientY;
31511         }
31512         var a = this.svgEl.dom.getScreenCTM();
31513         var b = a.inverse();
31514         var mx = pt.matrixTransform(b);
31515         return mx.x + ',' + mx.y;
31516     },
31517     //mouse event headler 
31518     down : function (e) {
31519         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31520         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31521         
31522         this.isMouseDown = true;
31523         
31524         e.preventDefault();
31525     },
31526     move : function (e) {
31527         if (this.isMouseDown) {
31528             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31529             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31530         }
31531         
31532         e.preventDefault();
31533     },
31534     up : function (e) {
31535         this.isMouseDown = false;
31536         var sp = this.signatureTmp.split(' ');
31537         
31538         if(sp.length > 1){
31539             if(!sp[sp.length-2].match(/^L/)){
31540                 sp.pop();
31541                 sp.pop();
31542                 sp.push("");
31543                 this.signatureTmp = sp.join(" ");
31544             }
31545         }
31546         if(this.getValue() != this.signatureTmp){
31547             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31548             this.isConfirmed = false;
31549         }
31550         e.preventDefault();
31551     },
31552     
31553     /**
31554      * Protected method that will not generally be called directly. It
31555      * is called when the editor creates its toolbar. Override this method if you need to
31556      * add custom toolbar buttons.
31557      * @param {HtmlEditor} editor
31558      */
31559     createToolbar : function(editor){
31560          function btn(id, toggle, handler){
31561             var xid = fid + '-'+ id ;
31562             return {
31563                 id : xid,
31564                 cmd : id,
31565                 cls : 'x-btn-icon x-edit-'+id,
31566                 enableToggle:toggle !== false,
31567                 scope: editor, // was editor...
31568                 handler:handler||editor.relayBtnCmd,
31569                 clickEvent:'mousedown',
31570                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31571                 tabIndex:-1
31572             };
31573         }
31574         
31575         
31576         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31577         this.tb = tb;
31578         this.tb.add(
31579            {
31580                 cls : ' x-signature-btn x-signature-'+id,
31581                 scope: editor, // was editor...
31582                 handler: this.reset,
31583                 clickEvent:'mousedown',
31584                 text: this.labels.clear
31585             },
31586             {
31587                  xtype : 'Fill',
31588                  xns: Roo.Toolbar
31589             }, 
31590             {
31591                 cls : '  x-signature-btn x-signature-'+id,
31592                 scope: editor, // was editor...
31593                 handler: this.confirmHandler,
31594                 clickEvent:'mousedown',
31595                 text: this.labels.confirm
31596             }
31597         );
31598     
31599     },
31600     //public
31601     /**
31602      * when user is clicked confirm then show this image.....
31603      * 
31604      * @return {String} Image Data URI
31605      */
31606     getImageDataURI : function(){
31607         var svg = this.svgEl.dom.parentNode.innerHTML;
31608         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31609         return src; 
31610     },
31611     /**
31612      * 
31613      * @return {Boolean} this.isConfirmed
31614      */
31615     getConfirmed : function(){
31616         return this.isConfirmed;
31617     },
31618     /**
31619      * 
31620      * @return {Number} this.width
31621      */
31622     getWidth : function(){
31623         return this.width;
31624     },
31625     /**
31626      * 
31627      * @return {Number} this.height
31628      */
31629     getHeight : function(){
31630         return this.height;
31631     },
31632     // private
31633     getSignature : function(){
31634         return this.signatureTmp;
31635     },
31636     // private
31637     reset : function(){
31638         this.signatureTmp = '';
31639         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31640         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31641         this.isConfirmed = false;
31642         Roo.form.Signature.superclass.reset.call(this);
31643     },
31644     setSignature : function(s){
31645         this.signatureTmp = s;
31646         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31647         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31648         this.setValue(s);
31649         this.isConfirmed = false;
31650         Roo.form.Signature.superclass.reset.call(this);
31651     }, 
31652     test : function(){
31653 //        Roo.log(this.signPanel.dom.contentWindow.up())
31654     },
31655     //private
31656     setConfirmed : function(){
31657         
31658         
31659         
31660 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31661     },
31662     // private
31663     confirmHandler : function(){
31664         if(!this.getSignature()){
31665             return;
31666         }
31667         
31668         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31669         this.setValue(this.getSignature());
31670         this.isConfirmed = true;
31671         
31672         this.fireEvent('confirm', this);
31673     },
31674     // private
31675     // Subclasses should provide the validation implementation by overriding this
31676     validateValue : function(value){
31677         if(this.allowBlank){
31678             return true;
31679         }
31680         
31681         if(this.isConfirmed){
31682             return true;
31683         }
31684         return false;
31685     }
31686 });/*
31687  * Based on:
31688  * Ext JS Library 1.1.1
31689  * Copyright(c) 2006-2007, Ext JS, LLC.
31690  *
31691  * Originally Released Under LGPL - original licence link has changed is not relivant.
31692  *
31693  * Fork - LGPL
31694  * <script type="text/javascript">
31695  */
31696  
31697
31698 /**
31699  * @class Roo.form.ComboBox
31700  * @extends Roo.form.TriggerField
31701  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31702  * @constructor
31703  * Create a new ComboBox.
31704  * @param {Object} config Configuration options
31705  */
31706 Roo.form.Select = function(config){
31707     Roo.form.Select.superclass.constructor.call(this, config);
31708      
31709 };
31710
31711 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31712     /**
31713      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31714      */
31715     /**
31716      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31717      * rendering into an Roo.Editor, defaults to false)
31718      */
31719     /**
31720      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31721      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31722      */
31723     /**
31724      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31725      */
31726     /**
31727      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31728      * the dropdown list (defaults to undefined, with no header element)
31729      */
31730
31731      /**
31732      * @cfg {String/Roo.Template} tpl The template to use to render the output
31733      */
31734      
31735     // private
31736     defaultAutoCreate : {tag: "select"  },
31737     /**
31738      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31739      */
31740     listWidth: undefined,
31741     /**
31742      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31743      * mode = 'remote' or 'text' if mode = 'local')
31744      */
31745     displayField: undefined,
31746     /**
31747      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31748      * mode = 'remote' or 'value' if mode = 'local'). 
31749      * Note: use of a valueField requires the user make a selection
31750      * in order for a value to be mapped.
31751      */
31752     valueField: undefined,
31753     
31754     
31755     /**
31756      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31757      * field's data value (defaults to the underlying DOM element's name)
31758      */
31759     hiddenName: undefined,
31760     /**
31761      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31762      */
31763     listClass: '',
31764     /**
31765      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31766      */
31767     selectedClass: 'x-combo-selected',
31768     /**
31769      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31770      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31771      * which displays a downward arrow icon).
31772      */
31773     triggerClass : 'x-form-arrow-trigger',
31774     /**
31775      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31776      */
31777     shadow:'sides',
31778     /**
31779      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31780      * anchor positions (defaults to 'tl-bl')
31781      */
31782     listAlign: 'tl-bl?',
31783     /**
31784      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31785      */
31786     maxHeight: 300,
31787     /**
31788      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31789      * query specified by the allQuery config option (defaults to 'query')
31790      */
31791     triggerAction: 'query',
31792     /**
31793      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31794      * (defaults to 4, does not apply if editable = false)
31795      */
31796     minChars : 4,
31797     /**
31798      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31799      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31800      */
31801     typeAhead: false,
31802     /**
31803      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31804      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31805      */
31806     queryDelay: 500,
31807     /**
31808      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31809      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31810      */
31811     pageSize: 0,
31812     /**
31813      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31814      * when editable = true (defaults to false)
31815      */
31816     selectOnFocus:false,
31817     /**
31818      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31819      */
31820     queryParam: 'query',
31821     /**
31822      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31823      * when mode = 'remote' (defaults to 'Loading...')
31824      */
31825     loadingText: 'Loading...',
31826     /**
31827      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31828      */
31829     resizable: false,
31830     /**
31831      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31832      */
31833     handleHeight : 8,
31834     /**
31835      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31836      * traditional select (defaults to true)
31837      */
31838     editable: true,
31839     /**
31840      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31841      */
31842     allQuery: '',
31843     /**
31844      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31845      */
31846     mode: 'remote',
31847     /**
31848      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31849      * listWidth has a higher value)
31850      */
31851     minListWidth : 70,
31852     /**
31853      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31854      * allow the user to set arbitrary text into the field (defaults to false)
31855      */
31856     forceSelection:false,
31857     /**
31858      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31859      * if typeAhead = true (defaults to 250)
31860      */
31861     typeAheadDelay : 250,
31862     /**
31863      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31864      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31865      */
31866     valueNotFoundText : undefined,
31867     
31868     /**
31869      * @cfg {String} defaultValue The value displayed after loading the store.
31870      */
31871     defaultValue: '',
31872     
31873     /**
31874      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31875      */
31876     blockFocus : false,
31877     
31878     /**
31879      * @cfg {Boolean} disableClear Disable showing of clear button.
31880      */
31881     disableClear : false,
31882     /**
31883      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31884      */
31885     alwaysQuery : false,
31886     
31887     //private
31888     addicon : false,
31889     editicon: false,
31890     
31891     // element that contains real text value.. (when hidden is used..)
31892      
31893     // private
31894     onRender : function(ct, position){
31895         Roo.form.Field.prototype.onRender.call(this, ct, position);
31896         
31897         if(this.store){
31898             this.store.on('beforeload', this.onBeforeLoad, this);
31899             this.store.on('load', this.onLoad, this);
31900             this.store.on('loadexception', this.onLoadException, this);
31901             this.store.load({});
31902         }
31903         
31904         
31905         
31906     },
31907
31908     // private
31909     initEvents : function(){
31910         //Roo.form.ComboBox.superclass.initEvents.call(this);
31911  
31912     },
31913
31914     onDestroy : function(){
31915        
31916         if(this.store){
31917             this.store.un('beforeload', this.onBeforeLoad, this);
31918             this.store.un('load', this.onLoad, this);
31919             this.store.un('loadexception', this.onLoadException, this);
31920         }
31921         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31922     },
31923
31924     // private
31925     fireKey : function(e){
31926         if(e.isNavKeyPress() && !this.list.isVisible()){
31927             this.fireEvent("specialkey", this, e);
31928         }
31929     },
31930
31931     // private
31932     onResize: function(w, h){
31933         
31934         return; 
31935     
31936         
31937     },
31938
31939     /**
31940      * Allow or prevent the user from directly editing the field text.  If false is passed,
31941      * the user will only be able to select from the items defined in the dropdown list.  This method
31942      * is the runtime equivalent of setting the 'editable' config option at config time.
31943      * @param {Boolean} value True to allow the user to directly edit the field text
31944      */
31945     setEditable : function(value){
31946          
31947     },
31948
31949     // private
31950     onBeforeLoad : function(){
31951         
31952         Roo.log("Select before load");
31953         return;
31954     
31955         this.innerList.update(this.loadingText ?
31956                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31957         //this.restrictHeight();
31958         this.selectedIndex = -1;
31959     },
31960
31961     // private
31962     onLoad : function(){
31963
31964     
31965         var dom = this.el.dom;
31966         dom.innerHTML = '';
31967          var od = dom.ownerDocument;
31968          
31969         if (this.emptyText) {
31970             var op = od.createElement('option');
31971             op.setAttribute('value', '');
31972             op.innerHTML = String.format('{0}', this.emptyText);
31973             dom.appendChild(op);
31974         }
31975         if(this.store.getCount() > 0){
31976            
31977             var vf = this.valueField;
31978             var df = this.displayField;
31979             this.store.data.each(function(r) {
31980                 // which colmsn to use... testing - cdoe / title..
31981                 var op = od.createElement('option');
31982                 op.setAttribute('value', r.data[vf]);
31983                 op.innerHTML = String.format('{0}', r.data[df]);
31984                 dom.appendChild(op);
31985             });
31986             if (typeof(this.defaultValue != 'undefined')) {
31987                 this.setValue(this.defaultValue);
31988             }
31989             
31990              
31991         }else{
31992             //this.onEmptyResults();
31993         }
31994         //this.el.focus();
31995     },
31996     // private
31997     onLoadException : function()
31998     {
31999         dom.innerHTML = '';
32000             
32001         Roo.log("Select on load exception");
32002         return;
32003     
32004         this.collapse();
32005         Roo.log(this.store.reader.jsonData);
32006         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32007             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32008         }
32009         
32010         
32011     },
32012     // private
32013     onTypeAhead : function(){
32014          
32015     },
32016
32017     // private
32018     onSelect : function(record, index){
32019         Roo.log('on select?');
32020         return;
32021         if(this.fireEvent('beforeselect', this, record, index) !== false){
32022             this.setFromData(index > -1 ? record.data : false);
32023             this.collapse();
32024             this.fireEvent('select', this, record, index);
32025         }
32026     },
32027
32028     /**
32029      * Returns the currently selected field value or empty string if no value is set.
32030      * @return {String} value The selected value
32031      */
32032     getValue : function(){
32033         var dom = this.el.dom;
32034         this.value = dom.options[dom.selectedIndex].value;
32035         return this.value;
32036         
32037     },
32038
32039     /**
32040      * Clears any text/value currently set in the field
32041      */
32042     clearValue : function(){
32043         this.value = '';
32044         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32045         
32046     },
32047
32048     /**
32049      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32050      * will be displayed in the field.  If the value does not match the data value of an existing item,
32051      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32052      * Otherwise the field will be blank (although the value will still be set).
32053      * @param {String} value The value to match
32054      */
32055     setValue : function(v){
32056         var d = this.el.dom;
32057         for (var i =0; i < d.options.length;i++) {
32058             if (v == d.options[i].value) {
32059                 d.selectedIndex = i;
32060                 this.value = v;
32061                 return;
32062             }
32063         }
32064         this.clearValue();
32065     },
32066     /**
32067      * @property {Object} the last set data for the element
32068      */
32069     
32070     lastData : false,
32071     /**
32072      * Sets the value of the field based on a object which is related to the record format for the store.
32073      * @param {Object} value the value to set as. or false on reset?
32074      */
32075     setFromData : function(o){
32076         Roo.log('setfrom data?');
32077          
32078         
32079         
32080     },
32081     // private
32082     reset : function(){
32083         this.clearValue();
32084     },
32085     // private
32086     findRecord : function(prop, value){
32087         
32088         return false;
32089     
32090         var record;
32091         if(this.store.getCount() > 0){
32092             this.store.each(function(r){
32093                 if(r.data[prop] == value){
32094                     record = r;
32095                     return false;
32096                 }
32097                 return true;
32098             });
32099         }
32100         return record;
32101     },
32102     
32103     getName: function()
32104     {
32105         // returns hidden if it's set..
32106         if (!this.rendered) {return ''};
32107         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32108         
32109     },
32110      
32111
32112     
32113
32114     // private
32115     onEmptyResults : function(){
32116         Roo.log('empty results');
32117         //this.collapse();
32118     },
32119
32120     /**
32121      * Returns true if the dropdown list is expanded, else false.
32122      */
32123     isExpanded : function(){
32124         return false;
32125     },
32126
32127     /**
32128      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32129      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32130      * @param {String} value The data value of the item to select
32131      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32132      * selected item if it is not currently in view (defaults to true)
32133      * @return {Boolean} True if the value matched an item in the list, else false
32134      */
32135     selectByValue : function(v, scrollIntoView){
32136         Roo.log('select By Value');
32137         return false;
32138     
32139         if(v !== undefined && v !== null){
32140             var r = this.findRecord(this.valueField || this.displayField, v);
32141             if(r){
32142                 this.select(this.store.indexOf(r), scrollIntoView);
32143                 return true;
32144             }
32145         }
32146         return false;
32147     },
32148
32149     /**
32150      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32151      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32152      * @param {Number} index The zero-based index of the list item to select
32153      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32154      * selected item if it is not currently in view (defaults to true)
32155      */
32156     select : function(index, scrollIntoView){
32157         Roo.log('select ');
32158         return  ;
32159         
32160         this.selectedIndex = index;
32161         this.view.select(index);
32162         if(scrollIntoView !== false){
32163             var el = this.view.getNode(index);
32164             if(el){
32165                 this.innerList.scrollChildIntoView(el, false);
32166             }
32167         }
32168     },
32169
32170       
32171
32172     // private
32173     validateBlur : function(){
32174         
32175         return;
32176         
32177     },
32178
32179     // private
32180     initQuery : function(){
32181         this.doQuery(this.getRawValue());
32182     },
32183
32184     // private
32185     doForce : function(){
32186         if(this.el.dom.value.length > 0){
32187             this.el.dom.value =
32188                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32189              
32190         }
32191     },
32192
32193     /**
32194      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32195      * query allowing the query action to be canceled if needed.
32196      * @param {String} query The SQL query to execute
32197      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32198      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32199      * saved in the current store (defaults to false)
32200      */
32201     doQuery : function(q, forceAll){
32202         
32203         Roo.log('doQuery?');
32204         if(q === undefined || q === null){
32205             q = '';
32206         }
32207         var qe = {
32208             query: q,
32209             forceAll: forceAll,
32210             combo: this,
32211             cancel:false
32212         };
32213         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32214             return false;
32215         }
32216         q = qe.query;
32217         forceAll = qe.forceAll;
32218         if(forceAll === true || (q.length >= this.minChars)){
32219             if(this.lastQuery != q || this.alwaysQuery){
32220                 this.lastQuery = q;
32221                 if(this.mode == 'local'){
32222                     this.selectedIndex = -1;
32223                     if(forceAll){
32224                         this.store.clearFilter();
32225                     }else{
32226                         this.store.filter(this.displayField, q);
32227                     }
32228                     this.onLoad();
32229                 }else{
32230                     this.store.baseParams[this.queryParam] = q;
32231                     this.store.load({
32232                         params: this.getParams(q)
32233                     });
32234                     this.expand();
32235                 }
32236             }else{
32237                 this.selectedIndex = -1;
32238                 this.onLoad();   
32239             }
32240         }
32241     },
32242
32243     // private
32244     getParams : function(q){
32245         var p = {};
32246         //p[this.queryParam] = q;
32247         if(this.pageSize){
32248             p.start = 0;
32249             p.limit = this.pageSize;
32250         }
32251         return p;
32252     },
32253
32254     /**
32255      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32256      */
32257     collapse : function(){
32258         
32259     },
32260
32261     // private
32262     collapseIf : function(e){
32263         
32264     },
32265
32266     /**
32267      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32268      */
32269     expand : function(){
32270         
32271     } ,
32272
32273     // private
32274      
32275
32276     /** 
32277     * @cfg {Boolean} grow 
32278     * @hide 
32279     */
32280     /** 
32281     * @cfg {Number} growMin 
32282     * @hide 
32283     */
32284     /** 
32285     * @cfg {Number} growMax 
32286     * @hide 
32287     */
32288     /**
32289      * @hide
32290      * @method autoSize
32291      */
32292     
32293     setWidth : function()
32294     {
32295         
32296     },
32297     getResizeEl : function(){
32298         return this.el;
32299     }
32300 });//<script type="text/javasscript">
32301  
32302
32303 /**
32304  * @class Roo.DDView
32305  * A DnD enabled version of Roo.View.
32306  * @param {Element/String} container The Element in which to create the View.
32307  * @param {String} tpl The template string used to create the markup for each element of the View
32308  * @param {Object} config The configuration properties. These include all the config options of
32309  * {@link Roo.View} plus some specific to this class.<br>
32310  * <p>
32311  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32312  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32313  * <p>
32314  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32315 .x-view-drag-insert-above {
32316         border-top:1px dotted #3366cc;
32317 }
32318 .x-view-drag-insert-below {
32319         border-bottom:1px dotted #3366cc;
32320 }
32321 </code></pre>
32322  * 
32323  */
32324  
32325 Roo.DDView = function(container, tpl, config) {
32326     Roo.DDView.superclass.constructor.apply(this, arguments);
32327     this.getEl().setStyle("outline", "0px none");
32328     this.getEl().unselectable();
32329     if (this.dragGroup) {
32330                 this.setDraggable(this.dragGroup.split(","));
32331     }
32332     if (this.dropGroup) {
32333                 this.setDroppable(this.dropGroup.split(","));
32334     }
32335     if (this.deletable) {
32336         this.setDeletable();
32337     }
32338     this.isDirtyFlag = false;
32339         this.addEvents({
32340                 "drop" : true
32341         });
32342 };
32343
32344 Roo.extend(Roo.DDView, Roo.View, {
32345 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32346 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32347 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32348 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32349
32350         isFormField: true,
32351
32352         reset: Roo.emptyFn,
32353         
32354         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32355
32356         validate: function() {
32357                 return true;
32358         },
32359         
32360         destroy: function() {
32361                 this.purgeListeners();
32362                 this.getEl.removeAllListeners();
32363                 this.getEl().remove();
32364                 if (this.dragZone) {
32365                         if (this.dragZone.destroy) {
32366                                 this.dragZone.destroy();
32367                         }
32368                 }
32369                 if (this.dropZone) {
32370                         if (this.dropZone.destroy) {
32371                                 this.dropZone.destroy();
32372                         }
32373                 }
32374         },
32375
32376 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32377         getName: function() {
32378                 return this.name;
32379         },
32380
32381 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32382         setValue: function(v) {
32383                 if (!this.store) {
32384                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32385                 }
32386                 var data = {};
32387                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32388                 this.store.proxy = new Roo.data.MemoryProxy(data);
32389                 this.store.load();
32390         },
32391
32392 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32393         getValue: function() {
32394                 var result = '(';
32395                 this.store.each(function(rec) {
32396                         result += rec.id + ',';
32397                 });
32398                 return result.substr(0, result.length - 1) + ')';
32399         },
32400         
32401         getIds: function() {
32402                 var i = 0, result = new Array(this.store.getCount());
32403                 this.store.each(function(rec) {
32404                         result[i++] = rec.id;
32405                 });
32406                 return result;
32407         },
32408         
32409         isDirty: function() {
32410                 return this.isDirtyFlag;
32411         },
32412
32413 /**
32414  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32415  *      whole Element becomes the target, and this causes the drop gesture to append.
32416  */
32417     getTargetFromEvent : function(e) {
32418                 var target = e.getTarget();
32419                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32420                 target = target.parentNode;
32421                 }
32422                 if (!target) {
32423                         target = this.el.dom.lastChild || this.el.dom;
32424                 }
32425                 return target;
32426     },
32427
32428 /**
32429  *      Create the drag data which consists of an object which has the property "ddel" as
32430  *      the drag proxy element. 
32431  */
32432     getDragData : function(e) {
32433         var target = this.findItemFromChild(e.getTarget());
32434                 if(target) {
32435                         this.handleSelection(e);
32436                         var selNodes = this.getSelectedNodes();
32437             var dragData = {
32438                 source: this,
32439                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32440                 nodes: selNodes,
32441                 records: []
32442                         };
32443                         var selectedIndices = this.getSelectedIndexes();
32444                         for (var i = 0; i < selectedIndices.length; i++) {
32445                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32446                         }
32447                         if (selNodes.length == 1) {
32448                                 dragData.ddel = target.cloneNode(true); // the div element
32449                         } else {
32450                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32451                                 div.className = 'multi-proxy';
32452                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32453                                         div.appendChild(selNodes[i].cloneNode(true));
32454                                 }
32455                                 dragData.ddel = div;
32456                         }
32457             //console.log(dragData)
32458             //console.log(dragData.ddel.innerHTML)
32459                         return dragData;
32460                 }
32461         //console.log('nodragData')
32462                 return false;
32463     },
32464     
32465 /**     Specify to which ddGroup items in this DDView may be dragged. */
32466     setDraggable: function(ddGroup) {
32467         if (ddGroup instanceof Array) {
32468                 Roo.each(ddGroup, this.setDraggable, this);
32469                 return;
32470         }
32471         if (this.dragZone) {
32472                 this.dragZone.addToGroup(ddGroup);
32473         } else {
32474                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32475                                 containerScroll: true,
32476                                 ddGroup: ddGroup 
32477
32478                         });
32479 //                      Draggability implies selection. DragZone's mousedown selects the element.
32480                         if (!this.multiSelect) { this.singleSelect = true; }
32481
32482 //                      Wire the DragZone's handlers up to methods in *this*
32483                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32484                 }
32485     },
32486
32487 /**     Specify from which ddGroup this DDView accepts drops. */
32488     setDroppable: function(ddGroup) {
32489         if (ddGroup instanceof Array) {
32490                 Roo.each(ddGroup, this.setDroppable, this);
32491                 return;
32492         }
32493         if (this.dropZone) {
32494                 this.dropZone.addToGroup(ddGroup);
32495         } else {
32496                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32497                                 containerScroll: true,
32498                                 ddGroup: ddGroup
32499                         });
32500
32501 //                      Wire the DropZone's handlers up to methods in *this*
32502                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32503                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32504                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32505                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32506                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32507                 }
32508     },
32509
32510 /**     Decide whether to drop above or below a View node. */
32511     getDropPoint : function(e, n, dd){
32512         if (n == this.el.dom) { return "above"; }
32513                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32514                 var c = t + (b - t) / 2;
32515                 var y = Roo.lib.Event.getPageY(e);
32516                 if(y <= c) {
32517                         return "above";
32518                 }else{
32519                         return "below";
32520                 }
32521     },
32522
32523     onNodeEnter : function(n, dd, e, data){
32524                 return false;
32525     },
32526     
32527     onNodeOver : function(n, dd, e, data){
32528                 var pt = this.getDropPoint(e, n, dd);
32529                 // set the insert point style on the target node
32530                 var dragElClass = this.dropNotAllowed;
32531                 if (pt) {
32532                         var targetElClass;
32533                         if (pt == "above"){
32534                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32535                                 targetElClass = "x-view-drag-insert-above";
32536                         } else {
32537                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32538                                 targetElClass = "x-view-drag-insert-below";
32539                         }
32540                         if (this.lastInsertClass != targetElClass){
32541                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32542                                 this.lastInsertClass = targetElClass;
32543                         }
32544                 }
32545                 return dragElClass;
32546         },
32547
32548     onNodeOut : function(n, dd, e, data){
32549                 this.removeDropIndicators(n);
32550     },
32551
32552     onNodeDrop : function(n, dd, e, data){
32553         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32554                 return false;
32555         }
32556         var pt = this.getDropPoint(e, n, dd);
32557                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32558                 if (pt == "below") { insertAt++; }
32559                 for (var i = 0; i < data.records.length; i++) {
32560                         var r = data.records[i];
32561                         var dup = this.store.getById(r.id);
32562                         if (dup && (dd != this.dragZone)) {
32563                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32564                         } else {
32565                                 if (data.copy) {
32566                                         this.store.insert(insertAt++, r.copy());
32567                                 } else {
32568                                         data.source.isDirtyFlag = true;
32569                                         r.store.remove(r);
32570                                         this.store.insert(insertAt++, r);
32571                                 }
32572                                 this.isDirtyFlag = true;
32573                         }
32574                 }
32575                 this.dragZone.cachedTarget = null;
32576                 return true;
32577     },
32578
32579     removeDropIndicators : function(n){
32580                 if(n){
32581                         Roo.fly(n).removeClass([
32582                                 "x-view-drag-insert-above",
32583                                 "x-view-drag-insert-below"]);
32584                         this.lastInsertClass = "_noclass";
32585                 }
32586     },
32587
32588 /**
32589  *      Utility method. Add a delete option to the DDView's context menu.
32590  *      @param {String} imageUrl The URL of the "delete" icon image.
32591  */
32592         setDeletable: function(imageUrl) {
32593                 if (!this.singleSelect && !this.multiSelect) {
32594                         this.singleSelect = true;
32595                 }
32596                 var c = this.getContextMenu();
32597                 this.contextMenu.on("itemclick", function(item) {
32598                         switch (item.id) {
32599                                 case "delete":
32600                                         this.remove(this.getSelectedIndexes());
32601                                         break;
32602                         }
32603                 }, this);
32604                 this.contextMenu.add({
32605                         icon: imageUrl,
32606                         id: "delete",
32607                         text: 'Delete'
32608                 });
32609         },
32610         
32611 /**     Return the context menu for this DDView. */
32612         getContextMenu: function() {
32613                 if (!this.contextMenu) {
32614 //                      Create the View's context menu
32615                         this.contextMenu = new Roo.menu.Menu({
32616                                 id: this.id + "-contextmenu"
32617                         });
32618                         this.el.on("contextmenu", this.showContextMenu, this);
32619                 }
32620                 return this.contextMenu;
32621         },
32622         
32623         disableContextMenu: function() {
32624                 if (this.contextMenu) {
32625                         this.el.un("contextmenu", this.showContextMenu, this);
32626                 }
32627         },
32628
32629         showContextMenu: function(e, item) {
32630         item = this.findItemFromChild(e.getTarget());
32631                 if (item) {
32632                         e.stopEvent();
32633                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32634                         this.contextMenu.showAt(e.getXY());
32635             }
32636     },
32637
32638 /**
32639  *      Remove {@link Roo.data.Record}s at the specified indices.
32640  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32641  */
32642     remove: function(selectedIndices) {
32643                 selectedIndices = [].concat(selectedIndices);
32644                 for (var i = 0; i < selectedIndices.length; i++) {
32645                         var rec = this.store.getAt(selectedIndices[i]);
32646                         this.store.remove(rec);
32647                 }
32648     },
32649
32650 /**
32651  *      Double click fires the event, but also, if this is draggable, and there is only one other
32652  *      related DropZone, it transfers the selected node.
32653  */
32654     onDblClick : function(e){
32655         var item = this.findItemFromChild(e.getTarget());
32656         if(item){
32657             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32658                 return false;
32659             }
32660             if (this.dragGroup) {
32661                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32662                     while (targets.indexOf(this.dropZone) > -1) {
32663                             targets.remove(this.dropZone);
32664                                 }
32665                     if (targets.length == 1) {
32666                                         this.dragZone.cachedTarget = null;
32667                         var el = Roo.get(targets[0].getEl());
32668                         var box = el.getBox(true);
32669                         targets[0].onNodeDrop(el.dom, {
32670                                 target: el.dom,
32671                                 xy: [box.x, box.y + box.height - 1]
32672                         }, null, this.getDragData(e));
32673                     }
32674                 }
32675         }
32676     },
32677     
32678     handleSelection: function(e) {
32679                 this.dragZone.cachedTarget = null;
32680         var item = this.findItemFromChild(e.getTarget());
32681         if (!item) {
32682                 this.clearSelections(true);
32683                 return;
32684         }
32685                 if (item && (this.multiSelect || this.singleSelect)){
32686                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32687                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32688                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32689                                 this.unselect(item);
32690                         } else {
32691                                 this.select(item, this.multiSelect && e.ctrlKey);
32692                                 this.lastSelection = item;
32693                         }
32694                 }
32695     },
32696
32697     onItemClick : function(item, index, e){
32698                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32699                         return false;
32700                 }
32701                 return true;
32702     },
32703
32704     unselect : function(nodeInfo, suppressEvent){
32705                 var node = this.getNode(nodeInfo);
32706                 if(node && this.isSelected(node)){
32707                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32708                                 Roo.fly(node).removeClass(this.selectedClass);
32709                                 this.selections.remove(node);
32710                                 if(!suppressEvent){
32711                                         this.fireEvent("selectionchange", this, this.selections);
32712                                 }
32713                         }
32714                 }
32715     }
32716 });
32717 /*
32718  * Based on:
32719  * Ext JS Library 1.1.1
32720  * Copyright(c) 2006-2007, Ext JS, LLC.
32721  *
32722  * Originally Released Under LGPL - original licence link has changed is not relivant.
32723  *
32724  * Fork - LGPL
32725  * <script type="text/javascript">
32726  */
32727  
32728 /**
32729  * @class Roo.LayoutManager
32730  * @extends Roo.util.Observable
32731  * Base class for layout managers.
32732  */
32733 Roo.LayoutManager = function(container, config){
32734     Roo.LayoutManager.superclass.constructor.call(this);
32735     this.el = Roo.get(container);
32736     // ie scrollbar fix
32737     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32738         document.body.scroll = "no";
32739     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32740         this.el.position('relative');
32741     }
32742     this.id = this.el.id;
32743     this.el.addClass("x-layout-container");
32744     /** false to disable window resize monitoring @type Boolean */
32745     this.monitorWindowResize = true;
32746     this.regions = {};
32747     this.addEvents({
32748         /**
32749          * @event layout
32750          * Fires when a layout is performed. 
32751          * @param {Roo.LayoutManager} this
32752          */
32753         "layout" : true,
32754         /**
32755          * @event regionresized
32756          * Fires when the user resizes a region. 
32757          * @param {Roo.LayoutRegion} region The resized region
32758          * @param {Number} newSize The new size (width for east/west, height for north/south)
32759          */
32760         "regionresized" : true,
32761         /**
32762          * @event regioncollapsed
32763          * Fires when a region is collapsed. 
32764          * @param {Roo.LayoutRegion} region The collapsed region
32765          */
32766         "regioncollapsed" : true,
32767         /**
32768          * @event regionexpanded
32769          * Fires when a region is expanded.  
32770          * @param {Roo.LayoutRegion} region The expanded region
32771          */
32772         "regionexpanded" : true
32773     });
32774     this.updating = false;
32775     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32776 };
32777
32778 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32779     /**
32780      * Returns true if this layout is currently being updated
32781      * @return {Boolean}
32782      */
32783     isUpdating : function(){
32784         return this.updating; 
32785     },
32786     
32787     /**
32788      * Suspend the LayoutManager from doing auto-layouts while
32789      * making multiple add or remove calls
32790      */
32791     beginUpdate : function(){
32792         this.updating = true;    
32793     },
32794     
32795     /**
32796      * Restore auto-layouts and optionally disable the manager from performing a layout
32797      * @param {Boolean} noLayout true to disable a layout update 
32798      */
32799     endUpdate : function(noLayout){
32800         this.updating = false;
32801         if(!noLayout){
32802             this.layout();
32803         }    
32804     },
32805     
32806     layout: function(){
32807         
32808     },
32809     
32810     onRegionResized : function(region, newSize){
32811         this.fireEvent("regionresized", region, newSize);
32812         this.layout();
32813     },
32814     
32815     onRegionCollapsed : function(region){
32816         this.fireEvent("regioncollapsed", region);
32817     },
32818     
32819     onRegionExpanded : function(region){
32820         this.fireEvent("regionexpanded", region);
32821     },
32822         
32823     /**
32824      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32825      * performs box-model adjustments.
32826      * @return {Object} The size as an object {width: (the width), height: (the height)}
32827      */
32828     getViewSize : function(){
32829         var size;
32830         if(this.el.dom != document.body){
32831             size = this.el.getSize();
32832         }else{
32833             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32834         }
32835         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32836         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32837         return size;
32838     },
32839     
32840     /**
32841      * Returns the Element this layout is bound to.
32842      * @return {Roo.Element}
32843      */
32844     getEl : function(){
32845         return this.el;
32846     },
32847     
32848     /**
32849      * Returns the specified region.
32850      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32851      * @return {Roo.LayoutRegion}
32852      */
32853     getRegion : function(target){
32854         return this.regions[target.toLowerCase()];
32855     },
32856     
32857     onWindowResize : function(){
32858         if(this.monitorWindowResize){
32859             this.layout();
32860         }
32861     }
32862 });/*
32863  * Based on:
32864  * Ext JS Library 1.1.1
32865  * Copyright(c) 2006-2007, Ext JS, LLC.
32866  *
32867  * Originally Released Under LGPL - original licence link has changed is not relivant.
32868  *
32869  * Fork - LGPL
32870  * <script type="text/javascript">
32871  */
32872 /**
32873  * @class Roo.BorderLayout
32874  * @extends Roo.LayoutManager
32875  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32876  * please see: <br><br>
32877  * <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>
32878  * <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>
32879  * Example:
32880  <pre><code>
32881  var layout = new Roo.BorderLayout(document.body, {
32882     north: {
32883         initialSize: 25,
32884         titlebar: false
32885     },
32886     west: {
32887         split:true,
32888         initialSize: 200,
32889         minSize: 175,
32890         maxSize: 400,
32891         titlebar: true,
32892         collapsible: true
32893     },
32894     east: {
32895         split:true,
32896         initialSize: 202,
32897         minSize: 175,
32898         maxSize: 400,
32899         titlebar: true,
32900         collapsible: true
32901     },
32902     south: {
32903         split:true,
32904         initialSize: 100,
32905         minSize: 100,
32906         maxSize: 200,
32907         titlebar: true,
32908         collapsible: true
32909     },
32910     center: {
32911         titlebar: true,
32912         autoScroll:true,
32913         resizeTabs: true,
32914         minTabWidth: 50,
32915         preferredTabWidth: 150
32916     }
32917 });
32918
32919 // shorthand
32920 var CP = Roo.ContentPanel;
32921
32922 layout.beginUpdate();
32923 layout.add("north", new CP("north", "North"));
32924 layout.add("south", new CP("south", {title: "South", closable: true}));
32925 layout.add("west", new CP("west", {title: "West"}));
32926 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32927 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32928 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32929 layout.getRegion("center").showPanel("center1");
32930 layout.endUpdate();
32931 </code></pre>
32932
32933 <b>The container the layout is rendered into can be either the body element or any other element.
32934 If it is not the body element, the container needs to either be an absolute positioned element,
32935 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32936 the container size if it is not the body element.</b>
32937
32938 * @constructor
32939 * Create a new BorderLayout
32940 * @param {String/HTMLElement/Element} container The container this layout is bound to
32941 * @param {Object} config Configuration options
32942  */
32943 Roo.BorderLayout = function(container, config){
32944     config = config || {};
32945     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32946     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32947     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32948         var target = this.factory.validRegions[i];
32949         if(config[target]){
32950             this.addRegion(target, config[target]);
32951         }
32952     }
32953 };
32954
32955 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32956     /**
32957      * Creates and adds a new region if it doesn't already exist.
32958      * @param {String} target The target region key (north, south, east, west or center).
32959      * @param {Object} config The regions config object
32960      * @return {BorderLayoutRegion} The new region
32961      */
32962     addRegion : function(target, config){
32963         if(!this.regions[target]){
32964             var r = this.factory.create(target, this, config);
32965             this.bindRegion(target, r);
32966         }
32967         return this.regions[target];
32968     },
32969
32970     // private (kinda)
32971     bindRegion : function(name, r){
32972         this.regions[name] = r;
32973         r.on("visibilitychange", this.layout, this);
32974         r.on("paneladded", this.layout, this);
32975         r.on("panelremoved", this.layout, this);
32976         r.on("invalidated", this.layout, this);
32977         r.on("resized", this.onRegionResized, this);
32978         r.on("collapsed", this.onRegionCollapsed, this);
32979         r.on("expanded", this.onRegionExpanded, this);
32980     },
32981
32982     /**
32983      * Performs a layout update.
32984      */
32985     layout : function(){
32986         if(this.updating) {
32987             return;
32988         }
32989         var size = this.getViewSize();
32990         var w = size.width;
32991         var h = size.height;
32992         var centerW = w;
32993         var centerH = h;
32994         var centerY = 0;
32995         var centerX = 0;
32996         //var x = 0, y = 0;
32997
32998         var rs = this.regions;
32999         var north = rs["north"];
33000         var south = rs["south"]; 
33001         var west = rs["west"];
33002         var east = rs["east"];
33003         var center = rs["center"];
33004         //if(this.hideOnLayout){ // not supported anymore
33005             //c.el.setStyle("display", "none");
33006         //}
33007         if(north && north.isVisible()){
33008             var b = north.getBox();
33009             var m = north.getMargins();
33010             b.width = w - (m.left+m.right);
33011             b.x = m.left;
33012             b.y = m.top;
33013             centerY = b.height + b.y + m.bottom;
33014             centerH -= centerY;
33015             north.updateBox(this.safeBox(b));
33016         }
33017         if(south && south.isVisible()){
33018             var b = south.getBox();
33019             var m = south.getMargins();
33020             b.width = w - (m.left+m.right);
33021             b.x = m.left;
33022             var totalHeight = (b.height + m.top + m.bottom);
33023             b.y = h - totalHeight + m.top;
33024             centerH -= totalHeight;
33025             south.updateBox(this.safeBox(b));
33026         }
33027         if(west && west.isVisible()){
33028             var b = west.getBox();
33029             var m = west.getMargins();
33030             b.height = centerH - (m.top+m.bottom);
33031             b.x = m.left;
33032             b.y = centerY + m.top;
33033             var totalWidth = (b.width + m.left + m.right);
33034             centerX += totalWidth;
33035             centerW -= totalWidth;
33036             west.updateBox(this.safeBox(b));
33037         }
33038         if(east && east.isVisible()){
33039             var b = east.getBox();
33040             var m = east.getMargins();
33041             b.height = centerH - (m.top+m.bottom);
33042             var totalWidth = (b.width + m.left + m.right);
33043             b.x = w - totalWidth + m.left;
33044             b.y = centerY + m.top;
33045             centerW -= totalWidth;
33046             east.updateBox(this.safeBox(b));
33047         }
33048         if(center){
33049             var m = center.getMargins();
33050             var centerBox = {
33051                 x: centerX + m.left,
33052                 y: centerY + m.top,
33053                 width: centerW - (m.left+m.right),
33054                 height: centerH - (m.top+m.bottom)
33055             };
33056             //if(this.hideOnLayout){
33057                 //center.el.setStyle("display", "block");
33058             //}
33059             center.updateBox(this.safeBox(centerBox));
33060         }
33061         this.el.repaint();
33062         this.fireEvent("layout", this);
33063     },
33064
33065     // private
33066     safeBox : function(box){
33067         box.width = Math.max(0, box.width);
33068         box.height = Math.max(0, box.height);
33069         return box;
33070     },
33071
33072     /**
33073      * Adds a ContentPanel (or subclass) to this layout.
33074      * @param {String} target The target region key (north, south, east, west or center).
33075      * @param {Roo.ContentPanel} panel The panel to add
33076      * @return {Roo.ContentPanel} The added panel
33077      */
33078     add : function(target, panel){
33079          
33080         target = target.toLowerCase();
33081         return this.regions[target].add(panel);
33082     },
33083
33084     /**
33085      * Remove a ContentPanel (or subclass) to this layout.
33086      * @param {String} target The target region key (north, south, east, west or center).
33087      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33088      * @return {Roo.ContentPanel} The removed panel
33089      */
33090     remove : function(target, panel){
33091         target = target.toLowerCase();
33092         return this.regions[target].remove(panel);
33093     },
33094
33095     /**
33096      * Searches all regions for a panel with the specified id
33097      * @param {String} panelId
33098      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33099      */
33100     findPanel : function(panelId){
33101         var rs = this.regions;
33102         for(var target in rs){
33103             if(typeof rs[target] != "function"){
33104                 var p = rs[target].getPanel(panelId);
33105                 if(p){
33106                     return p;
33107                 }
33108             }
33109         }
33110         return null;
33111     },
33112
33113     /**
33114      * Searches all regions for a panel with the specified id and activates (shows) it.
33115      * @param {String/ContentPanel} panelId The panels id or the panel itself
33116      * @return {Roo.ContentPanel} The shown panel or null
33117      */
33118     showPanel : function(panelId) {
33119       var rs = this.regions;
33120       for(var target in rs){
33121          var r = rs[target];
33122          if(typeof r != "function"){
33123             if(r.hasPanel(panelId)){
33124                return r.showPanel(panelId);
33125             }
33126          }
33127       }
33128       return null;
33129    },
33130
33131    /**
33132      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33133      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33134      */
33135     restoreState : function(provider){
33136         if(!provider){
33137             provider = Roo.state.Manager;
33138         }
33139         var sm = new Roo.LayoutStateManager();
33140         sm.init(this, provider);
33141     },
33142
33143     /**
33144      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33145      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33146      * a valid ContentPanel config object.  Example:
33147      * <pre><code>
33148 // Create the main layout
33149 var layout = new Roo.BorderLayout('main-ct', {
33150     west: {
33151         split:true,
33152         minSize: 175,
33153         titlebar: true
33154     },
33155     center: {
33156         title:'Components'
33157     }
33158 }, 'main-ct');
33159
33160 // Create and add multiple ContentPanels at once via configs
33161 layout.batchAdd({
33162    west: {
33163        id: 'source-files',
33164        autoCreate:true,
33165        title:'Ext Source Files',
33166        autoScroll:true,
33167        fitToFrame:true
33168    },
33169    center : {
33170        el: cview,
33171        autoScroll:true,
33172        fitToFrame:true,
33173        toolbar: tb,
33174        resizeEl:'cbody'
33175    }
33176 });
33177 </code></pre>
33178      * @param {Object} regions An object containing ContentPanel configs by region name
33179      */
33180     batchAdd : function(regions){
33181         this.beginUpdate();
33182         for(var rname in regions){
33183             var lr = this.regions[rname];
33184             if(lr){
33185                 this.addTypedPanels(lr, regions[rname]);
33186             }
33187         }
33188         this.endUpdate();
33189     },
33190
33191     // private
33192     addTypedPanels : function(lr, ps){
33193         if(typeof ps == 'string'){
33194             lr.add(new Roo.ContentPanel(ps));
33195         }
33196         else if(ps instanceof Array){
33197             for(var i =0, len = ps.length; i < len; i++){
33198                 this.addTypedPanels(lr, ps[i]);
33199             }
33200         }
33201         else if(!ps.events){ // raw config?
33202             var el = ps.el;
33203             delete ps.el; // prevent conflict
33204             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33205         }
33206         else {  // panel object assumed!
33207             lr.add(ps);
33208         }
33209     },
33210     /**
33211      * Adds a xtype elements to the layout.
33212      * <pre><code>
33213
33214 layout.addxtype({
33215        xtype : 'ContentPanel',
33216        region: 'west',
33217        items: [ .... ]
33218    }
33219 );
33220
33221 layout.addxtype({
33222         xtype : 'NestedLayoutPanel',
33223         region: 'west',
33224         layout: {
33225            center: { },
33226            west: { }   
33227         },
33228         items : [ ... list of content panels or nested layout panels.. ]
33229    }
33230 );
33231 </code></pre>
33232      * @param {Object} cfg Xtype definition of item to add.
33233      */
33234     addxtype : function(cfg)
33235     {
33236         // basically accepts a pannel...
33237         // can accept a layout region..!?!?
33238         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33239         
33240         if (!cfg.xtype.match(/Panel$/)) {
33241             return false;
33242         }
33243         var ret = false;
33244         
33245         if (typeof(cfg.region) == 'undefined') {
33246             Roo.log("Failed to add Panel, region was not set");
33247             Roo.log(cfg);
33248             return false;
33249         }
33250         var region = cfg.region;
33251         delete cfg.region;
33252         
33253           
33254         var xitems = [];
33255         if (cfg.items) {
33256             xitems = cfg.items;
33257             delete cfg.items;
33258         }
33259         var nb = false;
33260         
33261         switch(cfg.xtype) 
33262         {
33263             case 'ContentPanel':  // ContentPanel (el, cfg)
33264             case 'ScrollPanel':  // ContentPanel (el, cfg)
33265             case 'ViewPanel': 
33266                 if(cfg.autoCreate) {
33267                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33268                 } else {
33269                     var el = this.el.createChild();
33270                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33271                 }
33272                 
33273                 this.add(region, ret);
33274                 break;
33275             
33276             
33277             case 'TreePanel': // our new panel!
33278                 cfg.el = this.el.createChild();
33279                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33280                 this.add(region, ret);
33281                 break;
33282             
33283             case 'NestedLayoutPanel': 
33284                 // create a new Layout (which is  a Border Layout...
33285                 var el = this.el.createChild();
33286                 var clayout = cfg.layout;
33287                 delete cfg.layout;
33288                 clayout.items   = clayout.items  || [];
33289                 // replace this exitems with the clayout ones..
33290                 xitems = clayout.items;
33291                  
33292                 
33293                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33294                     cfg.background = false;
33295                 }
33296                 var layout = new Roo.BorderLayout(el, clayout);
33297                 
33298                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33299                 //console.log('adding nested layout panel '  + cfg.toSource());
33300                 this.add(region, ret);
33301                 nb = {}; /// find first...
33302                 break;
33303                 
33304             case 'GridPanel': 
33305             
33306                 // needs grid and region
33307                 
33308                 //var el = this.getRegion(region).el.createChild();
33309                 var el = this.el.createChild();
33310                 // create the grid first...
33311                 
33312                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33313                 delete cfg.grid;
33314                 if (region == 'center' && this.active ) {
33315                     cfg.background = false;
33316                 }
33317                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33318                 
33319                 this.add(region, ret);
33320                 if (cfg.background) {
33321                     ret.on('activate', function(gp) {
33322                         if (!gp.grid.rendered) {
33323                             gp.grid.render();
33324                         }
33325                     });
33326                 } else {
33327                     grid.render();
33328                 }
33329                 break;
33330            
33331            
33332            
33333                 
33334                 
33335                 
33336             default:
33337                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33338                     
33339                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33340                     this.add(region, ret);
33341                 } else {
33342                 
33343                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33344                     return null;
33345                 }
33346                 
33347              // GridPanel (grid, cfg)
33348             
33349         }
33350         this.beginUpdate();
33351         // add children..
33352         var region = '';
33353         var abn = {};
33354         Roo.each(xitems, function(i)  {
33355             region = nb && i.region ? i.region : false;
33356             
33357             var add = ret.addxtype(i);
33358            
33359             if (region) {
33360                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33361                 if (!i.background) {
33362                     abn[region] = nb[region] ;
33363                 }
33364             }
33365             
33366         });
33367         this.endUpdate();
33368
33369         // make the last non-background panel active..
33370         //if (nb) { Roo.log(abn); }
33371         if (nb) {
33372             
33373             for(var r in abn) {
33374                 region = this.getRegion(r);
33375                 if (region) {
33376                     // tried using nb[r], but it does not work..
33377                      
33378                     region.showPanel(abn[r]);
33379                    
33380                 }
33381             }
33382         }
33383         return ret;
33384         
33385     }
33386 });
33387
33388 /**
33389  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33390  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33391  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33392  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33393  * <pre><code>
33394 // shorthand
33395 var CP = Roo.ContentPanel;
33396
33397 var layout = Roo.BorderLayout.create({
33398     north: {
33399         initialSize: 25,
33400         titlebar: false,
33401         panels: [new CP("north", "North")]
33402     },
33403     west: {
33404         split:true,
33405         initialSize: 200,
33406         minSize: 175,
33407         maxSize: 400,
33408         titlebar: true,
33409         collapsible: true,
33410         panels: [new CP("west", {title: "West"})]
33411     },
33412     east: {
33413         split:true,
33414         initialSize: 202,
33415         minSize: 175,
33416         maxSize: 400,
33417         titlebar: true,
33418         collapsible: true,
33419         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33420     },
33421     south: {
33422         split:true,
33423         initialSize: 100,
33424         minSize: 100,
33425         maxSize: 200,
33426         titlebar: true,
33427         collapsible: true,
33428         panels: [new CP("south", {title: "South", closable: true})]
33429     },
33430     center: {
33431         titlebar: true,
33432         autoScroll:true,
33433         resizeTabs: true,
33434         minTabWidth: 50,
33435         preferredTabWidth: 150,
33436         panels: [
33437             new CP("center1", {title: "Close Me", closable: true}),
33438             new CP("center2", {title: "Center Panel", closable: false})
33439         ]
33440     }
33441 }, document.body);
33442
33443 layout.getRegion("center").showPanel("center1");
33444 </code></pre>
33445  * @param config
33446  * @param targetEl
33447  */
33448 Roo.BorderLayout.create = function(config, targetEl){
33449     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33450     layout.beginUpdate();
33451     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33452     for(var j = 0, jlen = regions.length; j < jlen; j++){
33453         var lr = regions[j];
33454         if(layout.regions[lr] && config[lr].panels){
33455             var r = layout.regions[lr];
33456             var ps = config[lr].panels;
33457             layout.addTypedPanels(r, ps);
33458         }
33459     }
33460     layout.endUpdate();
33461     return layout;
33462 };
33463
33464 // private
33465 Roo.BorderLayout.RegionFactory = {
33466     // private
33467     validRegions : ["north","south","east","west","center"],
33468
33469     // private
33470     create : function(target, mgr, config){
33471         target = target.toLowerCase();
33472         if(config.lightweight || config.basic){
33473             return new Roo.BasicLayoutRegion(mgr, config, target);
33474         }
33475         switch(target){
33476             case "north":
33477                 return new Roo.NorthLayoutRegion(mgr, config);
33478             case "south":
33479                 return new Roo.SouthLayoutRegion(mgr, config);
33480             case "east":
33481                 return new Roo.EastLayoutRegion(mgr, config);
33482             case "west":
33483                 return new Roo.WestLayoutRegion(mgr, config);
33484             case "center":
33485                 return new Roo.CenterLayoutRegion(mgr, config);
33486         }
33487         throw 'Layout region "'+target+'" not supported.';
33488     }
33489 };/*
33490  * Based on:
33491  * Ext JS Library 1.1.1
33492  * Copyright(c) 2006-2007, Ext JS, LLC.
33493  *
33494  * Originally Released Under LGPL - original licence link has changed is not relivant.
33495  *
33496  * Fork - LGPL
33497  * <script type="text/javascript">
33498  */
33499  
33500 /**
33501  * @class Roo.BasicLayoutRegion
33502  * @extends Roo.util.Observable
33503  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33504  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33505  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33506  */
33507 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33508     this.mgr = mgr;
33509     this.position  = pos;
33510     this.events = {
33511         /**
33512          * @scope Roo.BasicLayoutRegion
33513          */
33514         
33515         /**
33516          * @event beforeremove
33517          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33518          * @param {Roo.LayoutRegion} this
33519          * @param {Roo.ContentPanel} panel The panel
33520          * @param {Object} e The cancel event object
33521          */
33522         "beforeremove" : true,
33523         /**
33524          * @event invalidated
33525          * Fires when the layout for this region is changed.
33526          * @param {Roo.LayoutRegion} this
33527          */
33528         "invalidated" : true,
33529         /**
33530          * @event visibilitychange
33531          * Fires when this region is shown or hidden 
33532          * @param {Roo.LayoutRegion} this
33533          * @param {Boolean} visibility true or false
33534          */
33535         "visibilitychange" : true,
33536         /**
33537          * @event paneladded
33538          * Fires when a panel is added. 
33539          * @param {Roo.LayoutRegion} this
33540          * @param {Roo.ContentPanel} panel The panel
33541          */
33542         "paneladded" : true,
33543         /**
33544          * @event panelremoved
33545          * Fires when a panel is removed. 
33546          * @param {Roo.LayoutRegion} this
33547          * @param {Roo.ContentPanel} panel The panel
33548          */
33549         "panelremoved" : true,
33550         /**
33551          * @event collapsed
33552          * Fires when this region is collapsed.
33553          * @param {Roo.LayoutRegion} this
33554          */
33555         "collapsed" : true,
33556         /**
33557          * @event expanded
33558          * Fires when this region is expanded.
33559          * @param {Roo.LayoutRegion} this
33560          */
33561         "expanded" : true,
33562         /**
33563          * @event slideshow
33564          * Fires when this region is slid into view.
33565          * @param {Roo.LayoutRegion} this
33566          */
33567         "slideshow" : true,
33568         /**
33569          * @event slidehide
33570          * Fires when this region slides out of view. 
33571          * @param {Roo.LayoutRegion} this
33572          */
33573         "slidehide" : true,
33574         /**
33575          * @event panelactivated
33576          * Fires when a panel is activated. 
33577          * @param {Roo.LayoutRegion} this
33578          * @param {Roo.ContentPanel} panel The activated panel
33579          */
33580         "panelactivated" : true,
33581         /**
33582          * @event resized
33583          * Fires when the user resizes this region. 
33584          * @param {Roo.LayoutRegion} this
33585          * @param {Number} newSize The new size (width for east/west, height for north/south)
33586          */
33587         "resized" : true
33588     };
33589     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33590     this.panels = new Roo.util.MixedCollection();
33591     this.panels.getKey = this.getPanelId.createDelegate(this);
33592     this.box = null;
33593     this.activePanel = null;
33594     // ensure listeners are added...
33595     
33596     if (config.listeners || config.events) {
33597         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33598             listeners : config.listeners || {},
33599             events : config.events || {}
33600         });
33601     }
33602     
33603     if(skipConfig !== true){
33604         this.applyConfig(config);
33605     }
33606 };
33607
33608 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33609     getPanelId : function(p){
33610         return p.getId();
33611     },
33612     
33613     applyConfig : function(config){
33614         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33615         this.config = config;
33616         
33617     },
33618     
33619     /**
33620      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33621      * the width, for horizontal (north, south) the height.
33622      * @param {Number} newSize The new width or height
33623      */
33624     resizeTo : function(newSize){
33625         var el = this.el ? this.el :
33626                  (this.activePanel ? this.activePanel.getEl() : null);
33627         if(el){
33628             switch(this.position){
33629                 case "east":
33630                 case "west":
33631                     el.setWidth(newSize);
33632                     this.fireEvent("resized", this, newSize);
33633                 break;
33634                 case "north":
33635                 case "south":
33636                     el.setHeight(newSize);
33637                     this.fireEvent("resized", this, newSize);
33638                 break;                
33639             }
33640         }
33641     },
33642     
33643     getBox : function(){
33644         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33645     },
33646     
33647     getMargins : function(){
33648         return this.margins;
33649     },
33650     
33651     updateBox : function(box){
33652         this.box = box;
33653         var el = this.activePanel.getEl();
33654         el.dom.style.left = box.x + "px";
33655         el.dom.style.top = box.y + "px";
33656         this.activePanel.setSize(box.width, box.height);
33657     },
33658     
33659     /**
33660      * Returns the container element for this region.
33661      * @return {Roo.Element}
33662      */
33663     getEl : function(){
33664         return this.activePanel;
33665     },
33666     
33667     /**
33668      * Returns true if this region is currently visible.
33669      * @return {Boolean}
33670      */
33671     isVisible : function(){
33672         return this.activePanel ? true : false;
33673     },
33674     
33675     setActivePanel : function(panel){
33676         panel = this.getPanel(panel);
33677         if(this.activePanel && this.activePanel != panel){
33678             this.activePanel.setActiveState(false);
33679             this.activePanel.getEl().setLeftTop(-10000,-10000);
33680         }
33681         this.activePanel = panel;
33682         panel.setActiveState(true);
33683         if(this.box){
33684             panel.setSize(this.box.width, this.box.height);
33685         }
33686         this.fireEvent("panelactivated", this, panel);
33687         this.fireEvent("invalidated");
33688     },
33689     
33690     /**
33691      * Show the specified panel.
33692      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33693      * @return {Roo.ContentPanel} The shown panel or null
33694      */
33695     showPanel : function(panel){
33696         if(panel = this.getPanel(panel)){
33697             this.setActivePanel(panel);
33698         }
33699         return panel;
33700     },
33701     
33702     /**
33703      * Get the active panel for this region.
33704      * @return {Roo.ContentPanel} The active panel or null
33705      */
33706     getActivePanel : function(){
33707         return this.activePanel;
33708     },
33709     
33710     /**
33711      * Add the passed ContentPanel(s)
33712      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33713      * @return {Roo.ContentPanel} The panel added (if only one was added)
33714      */
33715     add : function(panel){
33716         if(arguments.length > 1){
33717             for(var i = 0, len = arguments.length; i < len; i++) {
33718                 this.add(arguments[i]);
33719             }
33720             return null;
33721         }
33722         if(this.hasPanel(panel)){
33723             this.showPanel(panel);
33724             return panel;
33725         }
33726         var el = panel.getEl();
33727         if(el.dom.parentNode != this.mgr.el.dom){
33728             this.mgr.el.dom.appendChild(el.dom);
33729         }
33730         if(panel.setRegion){
33731             panel.setRegion(this);
33732         }
33733         this.panels.add(panel);
33734         el.setStyle("position", "absolute");
33735         if(!panel.background){
33736             this.setActivePanel(panel);
33737             if(this.config.initialSize && this.panels.getCount()==1){
33738                 this.resizeTo(this.config.initialSize);
33739             }
33740         }
33741         this.fireEvent("paneladded", this, panel);
33742         return panel;
33743     },
33744     
33745     /**
33746      * Returns true if the panel is in this region.
33747      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33748      * @return {Boolean}
33749      */
33750     hasPanel : function(panel){
33751         if(typeof panel == "object"){ // must be panel obj
33752             panel = panel.getId();
33753         }
33754         return this.getPanel(panel) ? true : false;
33755     },
33756     
33757     /**
33758      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33759      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33760      * @param {Boolean} preservePanel Overrides the config preservePanel option
33761      * @return {Roo.ContentPanel} The panel that was removed
33762      */
33763     remove : function(panel, preservePanel){
33764         panel = this.getPanel(panel);
33765         if(!panel){
33766             return null;
33767         }
33768         var e = {};
33769         this.fireEvent("beforeremove", this, panel, e);
33770         if(e.cancel === true){
33771             return null;
33772         }
33773         var panelId = panel.getId();
33774         this.panels.removeKey(panelId);
33775         return panel;
33776     },
33777     
33778     /**
33779      * Returns the panel specified or null if it's not in this region.
33780      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33781      * @return {Roo.ContentPanel}
33782      */
33783     getPanel : function(id){
33784         if(typeof id == "object"){ // must be panel obj
33785             return id;
33786         }
33787         return this.panels.get(id);
33788     },
33789     
33790     /**
33791      * Returns this regions position (north/south/east/west/center).
33792      * @return {String} 
33793      */
33794     getPosition: function(){
33795         return this.position;    
33796     }
33797 });/*
33798  * Based on:
33799  * Ext JS Library 1.1.1
33800  * Copyright(c) 2006-2007, Ext JS, LLC.
33801  *
33802  * Originally Released Under LGPL - original licence link has changed is not relivant.
33803  *
33804  * Fork - LGPL
33805  * <script type="text/javascript">
33806  */
33807  
33808 /**
33809  * @class Roo.LayoutRegion
33810  * @extends Roo.BasicLayoutRegion
33811  * This class represents a region in a layout manager.
33812  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33813  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33814  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33815  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33816  * @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})
33817  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33818  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33819  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33820  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33821  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33822  * @cfg {String}    title           The title for the region (overrides panel titles)
33823  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33824  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33825  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33826  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33827  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33828  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33829  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33830  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33831  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33832  * @cfg {Boolean}   showPin         True to show a pin button
33833  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33834  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33835  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33836  * @cfg {Number}    width           For East/West panels
33837  * @cfg {Number}    height          For North/South panels
33838  * @cfg {Boolean}   split           To show the splitter
33839  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33840  */
33841 Roo.LayoutRegion = function(mgr, config, pos){
33842     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33843     var dh = Roo.DomHelper;
33844     /** This region's container element 
33845     * @type Roo.Element */
33846     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33847     /** This region's title element 
33848     * @type Roo.Element */
33849
33850     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33851         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33852         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33853     ]}, true);
33854     this.titleEl.enableDisplayMode();
33855     /** This region's title text element 
33856     * @type HTMLElement */
33857     this.titleTextEl = this.titleEl.dom.firstChild;
33858     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33859     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33860     this.closeBtn.enableDisplayMode();
33861     this.closeBtn.on("click", this.closeClicked, this);
33862     this.closeBtn.hide();
33863
33864     this.createBody(config);
33865     this.visible = true;
33866     this.collapsed = false;
33867
33868     if(config.hideWhenEmpty){
33869         this.hide();
33870         this.on("paneladded", this.validateVisibility, this);
33871         this.on("panelremoved", this.validateVisibility, this);
33872     }
33873     this.applyConfig(config);
33874 };
33875
33876 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33877
33878     createBody : function(){
33879         /** This region's body element 
33880         * @type Roo.Element */
33881         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33882     },
33883
33884     applyConfig : function(c){
33885         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33886             var dh = Roo.DomHelper;
33887             if(c.titlebar !== false){
33888                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33889                 this.collapseBtn.on("click", this.collapse, this);
33890                 this.collapseBtn.enableDisplayMode();
33891
33892                 if(c.showPin === true || this.showPin){
33893                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33894                     this.stickBtn.enableDisplayMode();
33895                     this.stickBtn.on("click", this.expand, this);
33896                     this.stickBtn.hide();
33897                 }
33898             }
33899             /** This region's collapsed element
33900             * @type Roo.Element */
33901             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33902                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33903             ]}, true);
33904             if(c.floatable !== false){
33905                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33906                this.collapsedEl.on("click", this.collapseClick, this);
33907             }
33908
33909             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33910                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33911                    id: "message", unselectable: "on", style:{"float":"left"}});
33912                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33913              }
33914             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33915             this.expandBtn.on("click", this.expand, this);
33916         }
33917         if(this.collapseBtn){
33918             this.collapseBtn.setVisible(c.collapsible == true);
33919         }
33920         this.cmargins = c.cmargins || this.cmargins ||
33921                          (this.position == "west" || this.position == "east" ?
33922                              {top: 0, left: 2, right:2, bottom: 0} :
33923                              {top: 2, left: 0, right:0, bottom: 2});
33924         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33925         this.bottomTabs = c.tabPosition != "top";
33926         this.autoScroll = c.autoScroll || false;
33927         if(this.autoScroll){
33928             this.bodyEl.setStyle("overflow", "auto");
33929         }else{
33930             this.bodyEl.setStyle("overflow", "hidden");
33931         }
33932         //if(c.titlebar !== false){
33933             if((!c.titlebar && !c.title) || c.titlebar === false){
33934                 this.titleEl.hide();
33935             }else{
33936                 this.titleEl.show();
33937                 if(c.title){
33938                     this.titleTextEl.innerHTML = c.title;
33939                 }
33940             }
33941         //}
33942         this.duration = c.duration || .30;
33943         this.slideDuration = c.slideDuration || .45;
33944         this.config = c;
33945         if(c.collapsed){
33946             this.collapse(true);
33947         }
33948         if(c.hidden){
33949             this.hide();
33950         }
33951     },
33952     /**
33953      * Returns true if this region is currently visible.
33954      * @return {Boolean}
33955      */
33956     isVisible : function(){
33957         return this.visible;
33958     },
33959
33960     /**
33961      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33962      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33963      */
33964     setCollapsedTitle : function(title){
33965         title = title || "&#160;";
33966         if(this.collapsedTitleTextEl){
33967             this.collapsedTitleTextEl.innerHTML = title;
33968         }
33969     },
33970
33971     getBox : function(){
33972         var b;
33973         if(!this.collapsed){
33974             b = this.el.getBox(false, true);
33975         }else{
33976             b = this.collapsedEl.getBox(false, true);
33977         }
33978         return b;
33979     },
33980
33981     getMargins : function(){
33982         return this.collapsed ? this.cmargins : this.margins;
33983     },
33984
33985     highlight : function(){
33986         this.el.addClass("x-layout-panel-dragover");
33987     },
33988
33989     unhighlight : function(){
33990         this.el.removeClass("x-layout-panel-dragover");
33991     },
33992
33993     updateBox : function(box){
33994         this.box = box;
33995         if(!this.collapsed){
33996             this.el.dom.style.left = box.x + "px";
33997             this.el.dom.style.top = box.y + "px";
33998             this.updateBody(box.width, box.height);
33999         }else{
34000             this.collapsedEl.dom.style.left = box.x + "px";
34001             this.collapsedEl.dom.style.top = box.y + "px";
34002             this.collapsedEl.setSize(box.width, box.height);
34003         }
34004         if(this.tabs){
34005             this.tabs.autoSizeTabs();
34006         }
34007     },
34008
34009     updateBody : function(w, h){
34010         if(w !== null){
34011             this.el.setWidth(w);
34012             w -= this.el.getBorderWidth("rl");
34013             if(this.config.adjustments){
34014                 w += this.config.adjustments[0];
34015             }
34016         }
34017         if(h !== null){
34018             this.el.setHeight(h);
34019             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34020             h -= this.el.getBorderWidth("tb");
34021             if(this.config.adjustments){
34022                 h += this.config.adjustments[1];
34023             }
34024             this.bodyEl.setHeight(h);
34025             if(this.tabs){
34026                 h = this.tabs.syncHeight(h);
34027             }
34028         }
34029         if(this.panelSize){
34030             w = w !== null ? w : this.panelSize.width;
34031             h = h !== null ? h : this.panelSize.height;
34032         }
34033         if(this.activePanel){
34034             var el = this.activePanel.getEl();
34035             w = w !== null ? w : el.getWidth();
34036             h = h !== null ? h : el.getHeight();
34037             this.panelSize = {width: w, height: h};
34038             this.activePanel.setSize(w, h);
34039         }
34040         if(Roo.isIE && this.tabs){
34041             this.tabs.el.repaint();
34042         }
34043     },
34044
34045     /**
34046      * Returns the container element for this region.
34047      * @return {Roo.Element}
34048      */
34049     getEl : function(){
34050         return this.el;
34051     },
34052
34053     /**
34054      * Hides this region.
34055      */
34056     hide : function(){
34057         if(!this.collapsed){
34058             this.el.dom.style.left = "-2000px";
34059             this.el.hide();
34060         }else{
34061             this.collapsedEl.dom.style.left = "-2000px";
34062             this.collapsedEl.hide();
34063         }
34064         this.visible = false;
34065         this.fireEvent("visibilitychange", this, false);
34066     },
34067
34068     /**
34069      * Shows this region if it was previously hidden.
34070      */
34071     show : function(){
34072         if(!this.collapsed){
34073             this.el.show();
34074         }else{
34075             this.collapsedEl.show();
34076         }
34077         this.visible = true;
34078         this.fireEvent("visibilitychange", this, true);
34079     },
34080
34081     closeClicked : function(){
34082         if(this.activePanel){
34083             this.remove(this.activePanel);
34084         }
34085     },
34086
34087     collapseClick : function(e){
34088         if(this.isSlid){
34089            e.stopPropagation();
34090            this.slideIn();
34091         }else{
34092            e.stopPropagation();
34093            this.slideOut();
34094         }
34095     },
34096
34097     /**
34098      * Collapses this region.
34099      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34100      */
34101     collapse : function(skipAnim){
34102         if(this.collapsed) {
34103             return;
34104         }
34105         this.collapsed = true;
34106         if(this.split){
34107             this.split.el.hide();
34108         }
34109         if(this.config.animate && skipAnim !== true){
34110             this.fireEvent("invalidated", this);
34111             this.animateCollapse();
34112         }else{
34113             this.el.setLocation(-20000,-20000);
34114             this.el.hide();
34115             this.collapsedEl.show();
34116             this.fireEvent("collapsed", this);
34117             this.fireEvent("invalidated", this);
34118         }
34119     },
34120
34121     animateCollapse : function(){
34122         // overridden
34123     },
34124
34125     /**
34126      * Expands this region if it was previously collapsed.
34127      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34128      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34129      */
34130     expand : function(e, skipAnim){
34131         if(e) {
34132             e.stopPropagation();
34133         }
34134         if(!this.collapsed || this.el.hasActiveFx()) {
34135             return;
34136         }
34137         if(this.isSlid){
34138             this.afterSlideIn();
34139             skipAnim = true;
34140         }
34141         this.collapsed = false;
34142         if(this.config.animate && skipAnim !== true){
34143             this.animateExpand();
34144         }else{
34145             this.el.show();
34146             if(this.split){
34147                 this.split.el.show();
34148             }
34149             this.collapsedEl.setLocation(-2000,-2000);
34150             this.collapsedEl.hide();
34151             this.fireEvent("invalidated", this);
34152             this.fireEvent("expanded", this);
34153         }
34154     },
34155
34156     animateExpand : function(){
34157         // overridden
34158     },
34159
34160     initTabs : function()
34161     {
34162         this.bodyEl.setStyle("overflow", "hidden");
34163         var ts = new Roo.TabPanel(
34164                 this.bodyEl.dom,
34165                 {
34166                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34167                     disableTooltips: this.config.disableTabTips,
34168                     toolbar : this.config.toolbar
34169                 }
34170         );
34171         if(this.config.hideTabs){
34172             ts.stripWrap.setDisplayed(false);
34173         }
34174         this.tabs = ts;
34175         ts.resizeTabs = this.config.resizeTabs === true;
34176         ts.minTabWidth = this.config.minTabWidth || 40;
34177         ts.maxTabWidth = this.config.maxTabWidth || 250;
34178         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34179         ts.monitorResize = false;
34180         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34181         ts.bodyEl.addClass('x-layout-tabs-body');
34182         this.panels.each(this.initPanelAsTab, this);
34183     },
34184
34185     initPanelAsTab : function(panel){
34186         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34187                     this.config.closeOnTab && panel.isClosable());
34188         if(panel.tabTip !== undefined){
34189             ti.setTooltip(panel.tabTip);
34190         }
34191         ti.on("activate", function(){
34192               this.setActivePanel(panel);
34193         }, this);
34194         if(this.config.closeOnTab){
34195             ti.on("beforeclose", function(t, e){
34196                 e.cancel = true;
34197                 this.remove(panel);
34198             }, this);
34199         }
34200         return ti;
34201     },
34202
34203     updatePanelTitle : function(panel, title){
34204         if(this.activePanel == panel){
34205             this.updateTitle(title);
34206         }
34207         if(this.tabs){
34208             var ti = this.tabs.getTab(panel.getEl().id);
34209             ti.setText(title);
34210             if(panel.tabTip !== undefined){
34211                 ti.setTooltip(panel.tabTip);
34212             }
34213         }
34214     },
34215
34216     updateTitle : function(title){
34217         if(this.titleTextEl && !this.config.title){
34218             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34219         }
34220     },
34221
34222     setActivePanel : function(panel){
34223         panel = this.getPanel(panel);
34224         if(this.activePanel && this.activePanel != panel){
34225             this.activePanel.setActiveState(false);
34226         }
34227         this.activePanel = panel;
34228         panel.setActiveState(true);
34229         if(this.panelSize){
34230             panel.setSize(this.panelSize.width, this.panelSize.height);
34231         }
34232         if(this.closeBtn){
34233             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34234         }
34235         this.updateTitle(panel.getTitle());
34236         if(this.tabs){
34237             this.fireEvent("invalidated", this);
34238         }
34239         this.fireEvent("panelactivated", this, panel);
34240     },
34241
34242     /**
34243      * Shows the specified panel.
34244      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34245      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34246      */
34247     showPanel : function(panel)
34248     {
34249         panel = this.getPanel(panel);
34250         if(panel){
34251             if(this.tabs){
34252                 var tab = this.tabs.getTab(panel.getEl().id);
34253                 if(tab.isHidden()){
34254                     this.tabs.unhideTab(tab.id);
34255                 }
34256                 tab.activate();
34257             }else{
34258                 this.setActivePanel(panel);
34259             }
34260         }
34261         return panel;
34262     },
34263
34264     /**
34265      * Get the active panel for this region.
34266      * @return {Roo.ContentPanel} The active panel or null
34267      */
34268     getActivePanel : function(){
34269         return this.activePanel;
34270     },
34271
34272     validateVisibility : function(){
34273         if(this.panels.getCount() < 1){
34274             this.updateTitle("&#160;");
34275             this.closeBtn.hide();
34276             this.hide();
34277         }else{
34278             if(!this.isVisible()){
34279                 this.show();
34280             }
34281         }
34282     },
34283
34284     /**
34285      * Adds the passed ContentPanel(s) to this region.
34286      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34287      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34288      */
34289     add : function(panel){
34290         if(arguments.length > 1){
34291             for(var i = 0, len = arguments.length; i < len; i++) {
34292                 this.add(arguments[i]);
34293             }
34294             return null;
34295         }
34296         if(this.hasPanel(panel)){
34297             this.showPanel(panel);
34298             return panel;
34299         }
34300         panel.setRegion(this);
34301         this.panels.add(panel);
34302         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34303             this.bodyEl.dom.appendChild(panel.getEl().dom);
34304             if(panel.background !== true){
34305                 this.setActivePanel(panel);
34306             }
34307             this.fireEvent("paneladded", this, panel);
34308             return panel;
34309         }
34310         if(!this.tabs){
34311             this.initTabs();
34312         }else{
34313             this.initPanelAsTab(panel);
34314         }
34315         if(panel.background !== true){
34316             this.tabs.activate(panel.getEl().id);
34317         }
34318         this.fireEvent("paneladded", this, panel);
34319         return panel;
34320     },
34321
34322     /**
34323      * Hides the tab for the specified panel.
34324      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34325      */
34326     hidePanel : function(panel){
34327         if(this.tabs && (panel = this.getPanel(panel))){
34328             this.tabs.hideTab(panel.getEl().id);
34329         }
34330     },
34331
34332     /**
34333      * Unhides the tab for a previously hidden panel.
34334      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34335      */
34336     unhidePanel : function(panel){
34337         if(this.tabs && (panel = this.getPanel(panel))){
34338             this.tabs.unhideTab(panel.getEl().id);
34339         }
34340     },
34341
34342     clearPanels : function(){
34343         while(this.panels.getCount() > 0){
34344              this.remove(this.panels.first());
34345         }
34346     },
34347
34348     /**
34349      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34350      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34351      * @param {Boolean} preservePanel Overrides the config preservePanel option
34352      * @return {Roo.ContentPanel} The panel that was removed
34353      */
34354     remove : function(panel, preservePanel){
34355         panel = this.getPanel(panel);
34356         if(!panel){
34357             return null;
34358         }
34359         var e = {};
34360         this.fireEvent("beforeremove", this, panel, e);
34361         if(e.cancel === true){
34362             return null;
34363         }
34364         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34365         var panelId = panel.getId();
34366         this.panels.removeKey(panelId);
34367         if(preservePanel){
34368             document.body.appendChild(panel.getEl().dom);
34369         }
34370         if(this.tabs){
34371             this.tabs.removeTab(panel.getEl().id);
34372         }else if (!preservePanel){
34373             this.bodyEl.dom.removeChild(panel.getEl().dom);
34374         }
34375         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34376             var p = this.panels.first();
34377             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34378             tempEl.appendChild(p.getEl().dom);
34379             this.bodyEl.update("");
34380             this.bodyEl.dom.appendChild(p.getEl().dom);
34381             tempEl = null;
34382             this.updateTitle(p.getTitle());
34383             this.tabs = null;
34384             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34385             this.setActivePanel(p);
34386         }
34387         panel.setRegion(null);
34388         if(this.activePanel == panel){
34389             this.activePanel = null;
34390         }
34391         if(this.config.autoDestroy !== false && preservePanel !== true){
34392             try{panel.destroy();}catch(e){}
34393         }
34394         this.fireEvent("panelremoved", this, panel);
34395         return panel;
34396     },
34397
34398     /**
34399      * Returns the TabPanel component used by this region
34400      * @return {Roo.TabPanel}
34401      */
34402     getTabs : function(){
34403         return this.tabs;
34404     },
34405
34406     createTool : function(parentEl, className){
34407         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34408             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34409         btn.addClassOnOver("x-layout-tools-button-over");
34410         return btn;
34411     }
34412 });/*
34413  * Based on:
34414  * Ext JS Library 1.1.1
34415  * Copyright(c) 2006-2007, Ext JS, LLC.
34416  *
34417  * Originally Released Under LGPL - original licence link has changed is not relivant.
34418  *
34419  * Fork - LGPL
34420  * <script type="text/javascript">
34421  */
34422  
34423
34424
34425 /**
34426  * @class Roo.SplitLayoutRegion
34427  * @extends Roo.LayoutRegion
34428  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34429  */
34430 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34431     this.cursor = cursor;
34432     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34433 };
34434
34435 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34436     splitTip : "Drag to resize.",
34437     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34438     useSplitTips : false,
34439
34440     applyConfig : function(config){
34441         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34442         if(config.split){
34443             if(!this.split){
34444                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34445                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34446                 /** The SplitBar for this region 
34447                 * @type Roo.SplitBar */
34448                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34449                 this.split.on("moved", this.onSplitMove, this);
34450                 this.split.useShim = config.useShim === true;
34451                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34452                 if(this.useSplitTips){
34453                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34454                 }
34455                 if(config.collapsible){
34456                     this.split.el.on("dblclick", this.collapse,  this);
34457                 }
34458             }
34459             if(typeof config.minSize != "undefined"){
34460                 this.split.minSize = config.minSize;
34461             }
34462             if(typeof config.maxSize != "undefined"){
34463                 this.split.maxSize = config.maxSize;
34464             }
34465             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34466                 this.hideSplitter();
34467             }
34468         }
34469     },
34470
34471     getHMaxSize : function(){
34472          var cmax = this.config.maxSize || 10000;
34473          var center = this.mgr.getRegion("center");
34474          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34475     },
34476
34477     getVMaxSize : function(){
34478          var cmax = this.config.maxSize || 10000;
34479          var center = this.mgr.getRegion("center");
34480          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34481     },
34482
34483     onSplitMove : function(split, newSize){
34484         this.fireEvent("resized", this, newSize);
34485     },
34486     
34487     /** 
34488      * Returns the {@link Roo.SplitBar} for this region.
34489      * @return {Roo.SplitBar}
34490      */
34491     getSplitBar : function(){
34492         return this.split;
34493     },
34494     
34495     hide : function(){
34496         this.hideSplitter();
34497         Roo.SplitLayoutRegion.superclass.hide.call(this);
34498     },
34499
34500     hideSplitter : function(){
34501         if(this.split){
34502             this.split.el.setLocation(-2000,-2000);
34503             this.split.el.hide();
34504         }
34505     },
34506
34507     show : function(){
34508         if(this.split){
34509             this.split.el.show();
34510         }
34511         Roo.SplitLayoutRegion.superclass.show.call(this);
34512     },
34513     
34514     beforeSlide: function(){
34515         if(Roo.isGecko){// firefox overflow auto bug workaround
34516             this.bodyEl.clip();
34517             if(this.tabs) {
34518                 this.tabs.bodyEl.clip();
34519             }
34520             if(this.activePanel){
34521                 this.activePanel.getEl().clip();
34522                 
34523                 if(this.activePanel.beforeSlide){
34524                     this.activePanel.beforeSlide();
34525                 }
34526             }
34527         }
34528     },
34529     
34530     afterSlide : function(){
34531         if(Roo.isGecko){// firefox overflow auto bug workaround
34532             this.bodyEl.unclip();
34533             if(this.tabs) {
34534                 this.tabs.bodyEl.unclip();
34535             }
34536             if(this.activePanel){
34537                 this.activePanel.getEl().unclip();
34538                 if(this.activePanel.afterSlide){
34539                     this.activePanel.afterSlide();
34540                 }
34541             }
34542         }
34543     },
34544
34545     initAutoHide : function(){
34546         if(this.autoHide !== false){
34547             if(!this.autoHideHd){
34548                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34549                 this.autoHideHd = {
34550                     "mouseout": function(e){
34551                         if(!e.within(this.el, true)){
34552                             st.delay(500);
34553                         }
34554                     },
34555                     "mouseover" : function(e){
34556                         st.cancel();
34557                     },
34558                     scope : this
34559                 };
34560             }
34561             this.el.on(this.autoHideHd);
34562         }
34563     },
34564
34565     clearAutoHide : function(){
34566         if(this.autoHide !== false){
34567             this.el.un("mouseout", this.autoHideHd.mouseout);
34568             this.el.un("mouseover", this.autoHideHd.mouseover);
34569         }
34570     },
34571
34572     clearMonitor : function(){
34573         Roo.get(document).un("click", this.slideInIf, this);
34574     },
34575
34576     // these names are backwards but not changed for compat
34577     slideOut : function(){
34578         if(this.isSlid || this.el.hasActiveFx()){
34579             return;
34580         }
34581         this.isSlid = true;
34582         if(this.collapseBtn){
34583             this.collapseBtn.hide();
34584         }
34585         this.closeBtnState = this.closeBtn.getStyle('display');
34586         this.closeBtn.hide();
34587         if(this.stickBtn){
34588             this.stickBtn.show();
34589         }
34590         this.el.show();
34591         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34592         this.beforeSlide();
34593         this.el.setStyle("z-index", 10001);
34594         this.el.slideIn(this.getSlideAnchor(), {
34595             callback: function(){
34596                 this.afterSlide();
34597                 this.initAutoHide();
34598                 Roo.get(document).on("click", this.slideInIf, this);
34599                 this.fireEvent("slideshow", this);
34600             },
34601             scope: this,
34602             block: true
34603         });
34604     },
34605
34606     afterSlideIn : function(){
34607         this.clearAutoHide();
34608         this.isSlid = false;
34609         this.clearMonitor();
34610         this.el.setStyle("z-index", "");
34611         if(this.collapseBtn){
34612             this.collapseBtn.show();
34613         }
34614         this.closeBtn.setStyle('display', this.closeBtnState);
34615         if(this.stickBtn){
34616             this.stickBtn.hide();
34617         }
34618         this.fireEvent("slidehide", this);
34619     },
34620
34621     slideIn : function(cb){
34622         if(!this.isSlid || this.el.hasActiveFx()){
34623             Roo.callback(cb);
34624             return;
34625         }
34626         this.isSlid = false;
34627         this.beforeSlide();
34628         this.el.slideOut(this.getSlideAnchor(), {
34629             callback: function(){
34630                 this.el.setLeftTop(-10000, -10000);
34631                 this.afterSlide();
34632                 this.afterSlideIn();
34633                 Roo.callback(cb);
34634             },
34635             scope: this,
34636             block: true
34637         });
34638     },
34639     
34640     slideInIf : function(e){
34641         if(!e.within(this.el)){
34642             this.slideIn();
34643         }
34644     },
34645
34646     animateCollapse : function(){
34647         this.beforeSlide();
34648         this.el.setStyle("z-index", 20000);
34649         var anchor = this.getSlideAnchor();
34650         this.el.slideOut(anchor, {
34651             callback : function(){
34652                 this.el.setStyle("z-index", "");
34653                 this.collapsedEl.slideIn(anchor, {duration:.3});
34654                 this.afterSlide();
34655                 this.el.setLocation(-10000,-10000);
34656                 this.el.hide();
34657                 this.fireEvent("collapsed", this);
34658             },
34659             scope: this,
34660             block: true
34661         });
34662     },
34663
34664     animateExpand : function(){
34665         this.beforeSlide();
34666         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34667         this.el.setStyle("z-index", 20000);
34668         this.collapsedEl.hide({
34669             duration:.1
34670         });
34671         this.el.slideIn(this.getSlideAnchor(), {
34672             callback : function(){
34673                 this.el.setStyle("z-index", "");
34674                 this.afterSlide();
34675                 if(this.split){
34676                     this.split.el.show();
34677                 }
34678                 this.fireEvent("invalidated", this);
34679                 this.fireEvent("expanded", this);
34680             },
34681             scope: this,
34682             block: true
34683         });
34684     },
34685
34686     anchors : {
34687         "west" : "left",
34688         "east" : "right",
34689         "north" : "top",
34690         "south" : "bottom"
34691     },
34692
34693     sanchors : {
34694         "west" : "l",
34695         "east" : "r",
34696         "north" : "t",
34697         "south" : "b"
34698     },
34699
34700     canchors : {
34701         "west" : "tl-tr",
34702         "east" : "tr-tl",
34703         "north" : "tl-bl",
34704         "south" : "bl-tl"
34705     },
34706
34707     getAnchor : function(){
34708         return this.anchors[this.position];
34709     },
34710
34711     getCollapseAnchor : function(){
34712         return this.canchors[this.position];
34713     },
34714
34715     getSlideAnchor : function(){
34716         return this.sanchors[this.position];
34717     },
34718
34719     getAlignAdj : function(){
34720         var cm = this.cmargins;
34721         switch(this.position){
34722             case "west":
34723                 return [0, 0];
34724             break;
34725             case "east":
34726                 return [0, 0];
34727             break;
34728             case "north":
34729                 return [0, 0];
34730             break;
34731             case "south":
34732                 return [0, 0];
34733             break;
34734         }
34735     },
34736
34737     getExpandAdj : function(){
34738         var c = this.collapsedEl, cm = this.cmargins;
34739         switch(this.position){
34740             case "west":
34741                 return [-(cm.right+c.getWidth()+cm.left), 0];
34742             break;
34743             case "east":
34744                 return [cm.right+c.getWidth()+cm.left, 0];
34745             break;
34746             case "north":
34747                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34748             break;
34749             case "south":
34750                 return [0, cm.top+cm.bottom+c.getHeight()];
34751             break;
34752         }
34753     }
34754 });/*
34755  * Based on:
34756  * Ext JS Library 1.1.1
34757  * Copyright(c) 2006-2007, Ext JS, LLC.
34758  *
34759  * Originally Released Under LGPL - original licence link has changed is not relivant.
34760  *
34761  * Fork - LGPL
34762  * <script type="text/javascript">
34763  */
34764 /*
34765  * These classes are private internal classes
34766  */
34767 Roo.CenterLayoutRegion = function(mgr, config){
34768     Roo.LayoutRegion.call(this, mgr, config, "center");
34769     this.visible = true;
34770     this.minWidth = config.minWidth || 20;
34771     this.minHeight = config.minHeight || 20;
34772 };
34773
34774 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34775     hide : function(){
34776         // center panel can't be hidden
34777     },
34778     
34779     show : function(){
34780         // center panel can't be hidden
34781     },
34782     
34783     getMinWidth: function(){
34784         return this.minWidth;
34785     },
34786     
34787     getMinHeight: function(){
34788         return this.minHeight;
34789     }
34790 });
34791
34792
34793 Roo.NorthLayoutRegion = function(mgr, config){
34794     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34795     if(this.split){
34796         this.split.placement = Roo.SplitBar.TOP;
34797         this.split.orientation = Roo.SplitBar.VERTICAL;
34798         this.split.el.addClass("x-layout-split-v");
34799     }
34800     var size = config.initialSize || config.height;
34801     if(typeof size != "undefined"){
34802         this.el.setHeight(size);
34803     }
34804 };
34805 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34806     orientation: Roo.SplitBar.VERTICAL,
34807     getBox : function(){
34808         if(this.collapsed){
34809             return this.collapsedEl.getBox();
34810         }
34811         var box = this.el.getBox();
34812         if(this.split){
34813             box.height += this.split.el.getHeight();
34814         }
34815         return box;
34816     },
34817     
34818     updateBox : function(box){
34819         if(this.split && !this.collapsed){
34820             box.height -= this.split.el.getHeight();
34821             this.split.el.setLeft(box.x);
34822             this.split.el.setTop(box.y+box.height);
34823             this.split.el.setWidth(box.width);
34824         }
34825         if(this.collapsed){
34826             this.updateBody(box.width, null);
34827         }
34828         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34829     }
34830 });
34831
34832 Roo.SouthLayoutRegion = function(mgr, config){
34833     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34834     if(this.split){
34835         this.split.placement = Roo.SplitBar.BOTTOM;
34836         this.split.orientation = Roo.SplitBar.VERTICAL;
34837         this.split.el.addClass("x-layout-split-v");
34838     }
34839     var size = config.initialSize || config.height;
34840     if(typeof size != "undefined"){
34841         this.el.setHeight(size);
34842     }
34843 };
34844 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34845     orientation: Roo.SplitBar.VERTICAL,
34846     getBox : function(){
34847         if(this.collapsed){
34848             return this.collapsedEl.getBox();
34849         }
34850         var box = this.el.getBox();
34851         if(this.split){
34852             var sh = this.split.el.getHeight();
34853             box.height += sh;
34854             box.y -= sh;
34855         }
34856         return box;
34857     },
34858     
34859     updateBox : function(box){
34860         if(this.split && !this.collapsed){
34861             var sh = this.split.el.getHeight();
34862             box.height -= sh;
34863             box.y += sh;
34864             this.split.el.setLeft(box.x);
34865             this.split.el.setTop(box.y-sh);
34866             this.split.el.setWidth(box.width);
34867         }
34868         if(this.collapsed){
34869             this.updateBody(box.width, null);
34870         }
34871         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34872     }
34873 });
34874
34875 Roo.EastLayoutRegion = function(mgr, config){
34876     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34877     if(this.split){
34878         this.split.placement = Roo.SplitBar.RIGHT;
34879         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34880         this.split.el.addClass("x-layout-split-h");
34881     }
34882     var size = config.initialSize || config.width;
34883     if(typeof size != "undefined"){
34884         this.el.setWidth(size);
34885     }
34886 };
34887 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34888     orientation: Roo.SplitBar.HORIZONTAL,
34889     getBox : function(){
34890         if(this.collapsed){
34891             return this.collapsedEl.getBox();
34892         }
34893         var box = this.el.getBox();
34894         if(this.split){
34895             var sw = this.split.el.getWidth();
34896             box.width += sw;
34897             box.x -= sw;
34898         }
34899         return box;
34900     },
34901
34902     updateBox : function(box){
34903         if(this.split && !this.collapsed){
34904             var sw = this.split.el.getWidth();
34905             box.width -= sw;
34906             this.split.el.setLeft(box.x);
34907             this.split.el.setTop(box.y);
34908             this.split.el.setHeight(box.height);
34909             box.x += sw;
34910         }
34911         if(this.collapsed){
34912             this.updateBody(null, box.height);
34913         }
34914         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34915     }
34916 });
34917
34918 Roo.WestLayoutRegion = function(mgr, config){
34919     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34920     if(this.split){
34921         this.split.placement = Roo.SplitBar.LEFT;
34922         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34923         this.split.el.addClass("x-layout-split-h");
34924     }
34925     var size = config.initialSize || config.width;
34926     if(typeof size != "undefined"){
34927         this.el.setWidth(size);
34928     }
34929 };
34930 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34931     orientation: Roo.SplitBar.HORIZONTAL,
34932     getBox : function(){
34933         if(this.collapsed){
34934             return this.collapsedEl.getBox();
34935         }
34936         var box = this.el.getBox();
34937         if(this.split){
34938             box.width += this.split.el.getWidth();
34939         }
34940         return box;
34941     },
34942     
34943     updateBox : function(box){
34944         if(this.split && !this.collapsed){
34945             var sw = this.split.el.getWidth();
34946             box.width -= sw;
34947             this.split.el.setLeft(box.x+box.width);
34948             this.split.el.setTop(box.y);
34949             this.split.el.setHeight(box.height);
34950         }
34951         if(this.collapsed){
34952             this.updateBody(null, box.height);
34953         }
34954         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34955     }
34956 });
34957 /*
34958  * Based on:
34959  * Ext JS Library 1.1.1
34960  * Copyright(c) 2006-2007, Ext JS, LLC.
34961  *
34962  * Originally Released Under LGPL - original licence link has changed is not relivant.
34963  *
34964  * Fork - LGPL
34965  * <script type="text/javascript">
34966  */
34967  
34968  
34969 /*
34970  * Private internal class for reading and applying state
34971  */
34972 Roo.LayoutStateManager = function(layout){
34973      // default empty state
34974      this.state = {
34975         north: {},
34976         south: {},
34977         east: {},
34978         west: {}       
34979     };
34980 };
34981
34982 Roo.LayoutStateManager.prototype = {
34983     init : function(layout, provider){
34984         this.provider = provider;
34985         var state = provider.get(layout.id+"-layout-state");
34986         if(state){
34987             var wasUpdating = layout.isUpdating();
34988             if(!wasUpdating){
34989                 layout.beginUpdate();
34990             }
34991             for(var key in state){
34992                 if(typeof state[key] != "function"){
34993                     var rstate = state[key];
34994                     var r = layout.getRegion(key);
34995                     if(r && rstate){
34996                         if(rstate.size){
34997                             r.resizeTo(rstate.size);
34998                         }
34999                         if(rstate.collapsed == true){
35000                             r.collapse(true);
35001                         }else{
35002                             r.expand(null, true);
35003                         }
35004                     }
35005                 }
35006             }
35007             if(!wasUpdating){
35008                 layout.endUpdate();
35009             }
35010             this.state = state; 
35011         }
35012         this.layout = layout;
35013         layout.on("regionresized", this.onRegionResized, this);
35014         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35015         layout.on("regionexpanded", this.onRegionExpanded, this);
35016     },
35017     
35018     storeState : function(){
35019         this.provider.set(this.layout.id+"-layout-state", this.state);
35020     },
35021     
35022     onRegionResized : function(region, newSize){
35023         this.state[region.getPosition()].size = newSize;
35024         this.storeState();
35025     },
35026     
35027     onRegionCollapsed : function(region){
35028         this.state[region.getPosition()].collapsed = true;
35029         this.storeState();
35030     },
35031     
35032     onRegionExpanded : function(region){
35033         this.state[region.getPosition()].collapsed = false;
35034         this.storeState();
35035     }
35036 };/*
35037  * Based on:
35038  * Ext JS Library 1.1.1
35039  * Copyright(c) 2006-2007, Ext JS, LLC.
35040  *
35041  * Originally Released Under LGPL - original licence link has changed is not relivant.
35042  *
35043  * Fork - LGPL
35044  * <script type="text/javascript">
35045  */
35046 /**
35047  * @class Roo.ContentPanel
35048  * @extends Roo.util.Observable
35049  * A basic ContentPanel element.
35050  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35051  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35052  * @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
35053  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35054  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35055  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35056  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35057  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35058  * @cfg {String} title          The title for this panel
35059  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35060  * @cfg {String} url            Calls {@link #setUrl} with this value
35061  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35062  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35063  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35064  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35065
35066  * @constructor
35067  * Create a new ContentPanel.
35068  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35069  * @param {String/Object} config A string to set only the title or a config object
35070  * @param {String} content (optional) Set the HTML content for this panel
35071  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35072  */
35073 Roo.ContentPanel = function(el, config, content){
35074     
35075      
35076     /*
35077     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35078         config = el;
35079         el = Roo.id();
35080     }
35081     if (config && config.parentLayout) { 
35082         el = config.parentLayout.el.createChild(); 
35083     }
35084     */
35085     if(el.autoCreate){ // xtype is available if this is called from factory
35086         config = el;
35087         el = Roo.id();
35088     }
35089     this.el = Roo.get(el);
35090     if(!this.el && config && config.autoCreate){
35091         if(typeof config.autoCreate == "object"){
35092             if(!config.autoCreate.id){
35093                 config.autoCreate.id = config.id||el;
35094             }
35095             this.el = Roo.DomHelper.append(document.body,
35096                         config.autoCreate, true);
35097         }else{
35098             this.el = Roo.DomHelper.append(document.body,
35099                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35100         }
35101     }
35102     this.closable = false;
35103     this.loaded = false;
35104     this.active = false;
35105     if(typeof config == "string"){
35106         this.title = config;
35107     }else{
35108         Roo.apply(this, config);
35109     }
35110     
35111     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35112         this.wrapEl = this.el.wrap();
35113         this.toolbar.container = this.el.insertSibling(false, 'before');
35114         this.toolbar = new Roo.Toolbar(this.toolbar);
35115     }
35116     
35117     // xtype created footer. - not sure if will work as we normally have to render first..
35118     if (this.footer && !this.footer.el && this.footer.xtype) {
35119         if (!this.wrapEl) {
35120             this.wrapEl = this.el.wrap();
35121         }
35122     
35123         this.footer.container = this.wrapEl.createChild();
35124          
35125         this.footer = Roo.factory(this.footer, Roo);
35126         
35127     }
35128     
35129     if(this.resizeEl){
35130         this.resizeEl = Roo.get(this.resizeEl, true);
35131     }else{
35132         this.resizeEl = this.el;
35133     }
35134     // handle view.xtype
35135     
35136  
35137     
35138     
35139     this.addEvents({
35140         /**
35141          * @event activate
35142          * Fires when this panel is activated. 
35143          * @param {Roo.ContentPanel} this
35144          */
35145         "activate" : true,
35146         /**
35147          * @event deactivate
35148          * Fires when this panel is activated. 
35149          * @param {Roo.ContentPanel} this
35150          */
35151         "deactivate" : true,
35152
35153         /**
35154          * @event resize
35155          * Fires when this panel is resized if fitToFrame is true.
35156          * @param {Roo.ContentPanel} this
35157          * @param {Number} width The width after any component adjustments
35158          * @param {Number} height The height after any component adjustments
35159          */
35160         "resize" : true,
35161         
35162          /**
35163          * @event render
35164          * Fires when this tab is created
35165          * @param {Roo.ContentPanel} this
35166          */
35167         "render" : true
35168         
35169         
35170         
35171     });
35172     
35173
35174     
35175     
35176     if(this.autoScroll){
35177         this.resizeEl.setStyle("overflow", "auto");
35178     } else {
35179         // fix randome scrolling
35180         this.el.on('scroll', function() {
35181             Roo.log('fix random scolling');
35182             this.scrollTo('top',0); 
35183         });
35184     }
35185     content = content || this.content;
35186     if(content){
35187         this.setContent(content);
35188     }
35189     if(config && config.url){
35190         this.setUrl(this.url, this.params, this.loadOnce);
35191     }
35192     
35193     
35194     
35195     Roo.ContentPanel.superclass.constructor.call(this);
35196     
35197     if (this.view && typeof(this.view.xtype) != 'undefined') {
35198         this.view.el = this.el.appendChild(document.createElement("div"));
35199         this.view = Roo.factory(this.view); 
35200         this.view.render  &&  this.view.render(false, '');  
35201     }
35202     
35203     
35204     this.fireEvent('render', this);
35205 };
35206
35207 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35208     tabTip:'',
35209     setRegion : function(region){
35210         this.region = region;
35211         if(region){
35212            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35213         }else{
35214            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35215         } 
35216     },
35217     
35218     /**
35219      * Returns the toolbar for this Panel if one was configured. 
35220      * @return {Roo.Toolbar} 
35221      */
35222     getToolbar : function(){
35223         return this.toolbar;
35224     },
35225     
35226     setActiveState : function(active){
35227         this.active = active;
35228         if(!active){
35229             this.fireEvent("deactivate", this);
35230         }else{
35231             this.fireEvent("activate", this);
35232         }
35233     },
35234     /**
35235      * Updates this panel's element
35236      * @param {String} content The new content
35237      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35238     */
35239     setContent : function(content, loadScripts){
35240         this.el.update(content, loadScripts);
35241     },
35242
35243     ignoreResize : function(w, h){
35244         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35245             return true;
35246         }else{
35247             this.lastSize = {width: w, height: h};
35248             return false;
35249         }
35250     },
35251     /**
35252      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35253      * @return {Roo.UpdateManager} The UpdateManager
35254      */
35255     getUpdateManager : function(){
35256         return this.el.getUpdateManager();
35257     },
35258      /**
35259      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35260      * @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:
35261 <pre><code>
35262 panel.load({
35263     url: "your-url.php",
35264     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35265     callback: yourFunction,
35266     scope: yourObject, //(optional scope)
35267     discardUrl: false,
35268     nocache: false,
35269     text: "Loading...",
35270     timeout: 30,
35271     scripts: false
35272 });
35273 </code></pre>
35274      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35275      * 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.
35276      * @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}
35277      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35278      * @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.
35279      * @return {Roo.ContentPanel} this
35280      */
35281     load : function(){
35282         var um = this.el.getUpdateManager();
35283         um.update.apply(um, arguments);
35284         return this;
35285     },
35286
35287
35288     /**
35289      * 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.
35290      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35291      * @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)
35292      * @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)
35293      * @return {Roo.UpdateManager} The UpdateManager
35294      */
35295     setUrl : function(url, params, loadOnce){
35296         if(this.refreshDelegate){
35297             this.removeListener("activate", this.refreshDelegate);
35298         }
35299         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35300         this.on("activate", this.refreshDelegate);
35301         return this.el.getUpdateManager();
35302     },
35303     
35304     _handleRefresh : function(url, params, loadOnce){
35305         if(!loadOnce || !this.loaded){
35306             var updater = this.el.getUpdateManager();
35307             updater.update(url, params, this._setLoaded.createDelegate(this));
35308         }
35309     },
35310     
35311     _setLoaded : function(){
35312         this.loaded = true;
35313     }, 
35314     
35315     /**
35316      * Returns this panel's id
35317      * @return {String} 
35318      */
35319     getId : function(){
35320         return this.el.id;
35321     },
35322     
35323     /** 
35324      * Returns this panel's element - used by regiosn to add.
35325      * @return {Roo.Element} 
35326      */
35327     getEl : function(){
35328         return this.wrapEl || this.el;
35329     },
35330     
35331     adjustForComponents : function(width, height)
35332     {
35333         //Roo.log('adjustForComponents ');
35334         if(this.resizeEl != this.el){
35335             width -= this.el.getFrameWidth('lr');
35336             height -= this.el.getFrameWidth('tb');
35337         }
35338         if(this.toolbar){
35339             var te = this.toolbar.getEl();
35340             height -= te.getHeight();
35341             te.setWidth(width);
35342         }
35343         if(this.footer){
35344             var te = this.footer.getEl();
35345             Roo.log("footer:" + te.getHeight());
35346             
35347             height -= te.getHeight();
35348             te.setWidth(width);
35349         }
35350         
35351         
35352         if(this.adjustments){
35353             width += this.adjustments[0];
35354             height += this.adjustments[1];
35355         }
35356         return {"width": width, "height": height};
35357     },
35358     
35359     setSize : function(width, height){
35360         if(this.fitToFrame && !this.ignoreResize(width, height)){
35361             if(this.fitContainer && this.resizeEl != this.el){
35362                 this.el.setSize(width, height);
35363             }
35364             var size = this.adjustForComponents(width, height);
35365             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35366             this.fireEvent('resize', this, size.width, size.height);
35367         }
35368     },
35369     
35370     /**
35371      * Returns this panel's title
35372      * @return {String} 
35373      */
35374     getTitle : function(){
35375         return this.title;
35376     },
35377     
35378     /**
35379      * Set this panel's title
35380      * @param {String} title
35381      */
35382     setTitle : function(title){
35383         this.title = title;
35384         if(this.region){
35385             this.region.updatePanelTitle(this, title);
35386         }
35387     },
35388     
35389     /**
35390      * Returns true is this panel was configured to be closable
35391      * @return {Boolean} 
35392      */
35393     isClosable : function(){
35394         return this.closable;
35395     },
35396     
35397     beforeSlide : function(){
35398         this.el.clip();
35399         this.resizeEl.clip();
35400     },
35401     
35402     afterSlide : function(){
35403         this.el.unclip();
35404         this.resizeEl.unclip();
35405     },
35406     
35407     /**
35408      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35409      *   Will fail silently if the {@link #setUrl} method has not been called.
35410      *   This does not activate the panel, just updates its content.
35411      */
35412     refresh : function(){
35413         if(this.refreshDelegate){
35414            this.loaded = false;
35415            this.refreshDelegate();
35416         }
35417     },
35418     
35419     /**
35420      * Destroys this panel
35421      */
35422     destroy : function(){
35423         this.el.removeAllListeners();
35424         var tempEl = document.createElement("span");
35425         tempEl.appendChild(this.el.dom);
35426         tempEl.innerHTML = "";
35427         this.el.remove();
35428         this.el = null;
35429     },
35430     
35431     /**
35432      * form - if the content panel contains a form - this is a reference to it.
35433      * @type {Roo.form.Form}
35434      */
35435     form : false,
35436     /**
35437      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35438      *    This contains a reference to it.
35439      * @type {Roo.View}
35440      */
35441     view : false,
35442     
35443       /**
35444      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35445      * <pre><code>
35446
35447 layout.addxtype({
35448        xtype : 'Form',
35449        items: [ .... ]
35450    }
35451 );
35452
35453 </code></pre>
35454      * @param {Object} cfg Xtype definition of item to add.
35455      */
35456     
35457     addxtype : function(cfg) {
35458         // add form..
35459         if (cfg.xtype.match(/^Form$/)) {
35460             
35461             var el;
35462             //if (this.footer) {
35463             //    el = this.footer.container.insertSibling(false, 'before');
35464             //} else {
35465                 el = this.el.createChild();
35466             //}
35467
35468             this.form = new  Roo.form.Form(cfg);
35469             
35470             
35471             if ( this.form.allItems.length) {
35472                 this.form.render(el.dom);
35473             }
35474             return this.form;
35475         }
35476         // should only have one of theses..
35477         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35478             // views.. should not be just added - used named prop 'view''
35479             
35480             cfg.el = this.el.appendChild(document.createElement("div"));
35481             // factory?
35482             
35483             var ret = new Roo.factory(cfg);
35484              
35485              ret.render && ret.render(false, ''); // render blank..
35486             this.view = ret;
35487             return ret;
35488         }
35489         return false;
35490     }
35491 });
35492
35493 /**
35494  * @class Roo.GridPanel
35495  * @extends Roo.ContentPanel
35496  * @constructor
35497  * Create a new GridPanel.
35498  * @param {Roo.grid.Grid} grid The grid for this panel
35499  * @param {String/Object} config A string to set only the panel's title, or a config object
35500  */
35501 Roo.GridPanel = function(grid, config){
35502     
35503   
35504     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35505         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35506         
35507     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35508     
35509     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35510     
35511     if(this.toolbar){
35512         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35513     }
35514     // xtype created footer. - not sure if will work as we normally have to render first..
35515     if (this.footer && !this.footer.el && this.footer.xtype) {
35516         
35517         this.footer.container = this.grid.getView().getFooterPanel(true);
35518         this.footer.dataSource = this.grid.dataSource;
35519         this.footer = Roo.factory(this.footer, Roo);
35520         
35521     }
35522     
35523     grid.monitorWindowResize = false; // turn off autosizing
35524     grid.autoHeight = false;
35525     grid.autoWidth = false;
35526     this.grid = grid;
35527     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35528 };
35529
35530 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35531     getId : function(){
35532         return this.grid.id;
35533     },
35534     
35535     /**
35536      * Returns the grid for this panel
35537      * @return {Roo.grid.Grid} 
35538      */
35539     getGrid : function(){
35540         return this.grid;    
35541     },
35542     
35543     setSize : function(width, height){
35544         if(!this.ignoreResize(width, height)){
35545             var grid = this.grid;
35546             var size = this.adjustForComponents(width, height);
35547             grid.getGridEl().setSize(size.width, size.height);
35548             grid.autoSize();
35549         }
35550     },
35551     
35552     beforeSlide : function(){
35553         this.grid.getView().scroller.clip();
35554     },
35555     
35556     afterSlide : function(){
35557         this.grid.getView().scroller.unclip();
35558     },
35559     
35560     destroy : function(){
35561         this.grid.destroy();
35562         delete this.grid;
35563         Roo.GridPanel.superclass.destroy.call(this); 
35564     }
35565 });
35566
35567
35568 /**
35569  * @class Roo.NestedLayoutPanel
35570  * @extends Roo.ContentPanel
35571  * @constructor
35572  * Create a new NestedLayoutPanel.
35573  * 
35574  * 
35575  * @param {Roo.BorderLayout} layout The layout for this panel
35576  * @param {String/Object} config A string to set only the title or a config object
35577  */
35578 Roo.NestedLayoutPanel = function(layout, config)
35579 {
35580     // construct with only one argument..
35581     /* FIXME - implement nicer consturctors
35582     if (layout.layout) {
35583         config = layout;
35584         layout = config.layout;
35585         delete config.layout;
35586     }
35587     if (layout.xtype && !layout.getEl) {
35588         // then layout needs constructing..
35589         layout = Roo.factory(layout, Roo);
35590     }
35591     */
35592     
35593     
35594     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35595     
35596     layout.monitorWindowResize = false; // turn off autosizing
35597     this.layout = layout;
35598     this.layout.getEl().addClass("x-layout-nested-layout");
35599     
35600     
35601     
35602     
35603 };
35604
35605 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35606
35607     setSize : function(width, height){
35608         if(!this.ignoreResize(width, height)){
35609             var size = this.adjustForComponents(width, height);
35610             var el = this.layout.getEl();
35611             el.setSize(size.width, size.height);
35612             var touch = el.dom.offsetWidth;
35613             this.layout.layout();
35614             // ie requires a double layout on the first pass
35615             if(Roo.isIE && !this.initialized){
35616                 this.initialized = true;
35617                 this.layout.layout();
35618             }
35619         }
35620     },
35621     
35622     // activate all subpanels if not currently active..
35623     
35624     setActiveState : function(active){
35625         this.active = active;
35626         if(!active){
35627             this.fireEvent("deactivate", this);
35628             return;
35629         }
35630         
35631         this.fireEvent("activate", this);
35632         // not sure if this should happen before or after..
35633         if (!this.layout) {
35634             return; // should not happen..
35635         }
35636         var reg = false;
35637         for (var r in this.layout.regions) {
35638             reg = this.layout.getRegion(r);
35639             if (reg.getActivePanel()) {
35640                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35641                 reg.setActivePanel(reg.getActivePanel());
35642                 continue;
35643             }
35644             if (!reg.panels.length) {
35645                 continue;
35646             }
35647             reg.showPanel(reg.getPanel(0));
35648         }
35649         
35650         
35651         
35652         
35653     },
35654     
35655     /**
35656      * Returns the nested BorderLayout for this panel
35657      * @return {Roo.BorderLayout} 
35658      */
35659     getLayout : function(){
35660         return this.layout;
35661     },
35662     
35663      /**
35664      * Adds a xtype elements to the layout of the nested panel
35665      * <pre><code>
35666
35667 panel.addxtype({
35668        xtype : 'ContentPanel',
35669        region: 'west',
35670        items: [ .... ]
35671    }
35672 );
35673
35674 panel.addxtype({
35675         xtype : 'NestedLayoutPanel',
35676         region: 'west',
35677         layout: {
35678            center: { },
35679            west: { }   
35680         },
35681         items : [ ... list of content panels or nested layout panels.. ]
35682    }
35683 );
35684 </code></pre>
35685      * @param {Object} cfg Xtype definition of item to add.
35686      */
35687     addxtype : function(cfg) {
35688         return this.layout.addxtype(cfg);
35689     
35690     }
35691 });
35692
35693 Roo.ScrollPanel = function(el, config, content){
35694     config = config || {};
35695     config.fitToFrame = true;
35696     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35697     
35698     this.el.dom.style.overflow = "hidden";
35699     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35700     this.el.removeClass("x-layout-inactive-content");
35701     this.el.on("mousewheel", this.onWheel, this);
35702
35703     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35704     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35705     up.unselectable(); down.unselectable();
35706     up.on("click", this.scrollUp, this);
35707     down.on("click", this.scrollDown, this);
35708     up.addClassOnOver("x-scroller-btn-over");
35709     down.addClassOnOver("x-scroller-btn-over");
35710     up.addClassOnClick("x-scroller-btn-click");
35711     down.addClassOnClick("x-scroller-btn-click");
35712     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35713
35714     this.resizeEl = this.el;
35715     this.el = wrap; this.up = up; this.down = down;
35716 };
35717
35718 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35719     increment : 100,
35720     wheelIncrement : 5,
35721     scrollUp : function(){
35722         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35723     },
35724
35725     scrollDown : function(){
35726         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35727     },
35728
35729     afterScroll : function(){
35730         var el = this.resizeEl;
35731         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35732         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35733         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35734     },
35735
35736     setSize : function(){
35737         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35738         this.afterScroll();
35739     },
35740
35741     onWheel : function(e){
35742         var d = e.getWheelDelta();
35743         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35744         this.afterScroll();
35745         e.stopEvent();
35746     },
35747
35748     setContent : function(content, loadScripts){
35749         this.resizeEl.update(content, loadScripts);
35750     }
35751
35752 });
35753
35754
35755
35756
35757
35758
35759
35760
35761
35762 /**
35763  * @class Roo.TreePanel
35764  * @extends Roo.ContentPanel
35765  * @constructor
35766  * Create a new TreePanel. - defaults to fit/scoll contents.
35767  * @param {String/Object} config A string to set only the panel's title, or a config object
35768  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35769  */
35770 Roo.TreePanel = function(config){
35771     var el = config.el;
35772     var tree = config.tree;
35773     delete config.tree; 
35774     delete config.el; // hopefull!
35775     
35776     // wrapper for IE7 strict & safari scroll issue
35777     
35778     var treeEl = el.createChild();
35779     config.resizeEl = treeEl;
35780     
35781     
35782     
35783     Roo.TreePanel.superclass.constructor.call(this, el, config);
35784  
35785  
35786     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35787     //console.log(tree);
35788     this.on('activate', function()
35789     {
35790         if (this.tree.rendered) {
35791             return;
35792         }
35793         //console.log('render tree');
35794         this.tree.render();
35795     });
35796     // this should not be needed.. - it's actually the 'el' that resizes?
35797     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35798     
35799     //this.on('resize',  function (cp, w, h) {
35800     //        this.tree.innerCt.setWidth(w);
35801     //        this.tree.innerCt.setHeight(h);
35802     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35803     //});
35804
35805         
35806     
35807 };
35808
35809 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35810     fitToFrame : true,
35811     autoScroll : true
35812 });
35813
35814
35815
35816
35817
35818
35819
35820
35821
35822
35823
35824 /*
35825  * Based on:
35826  * Ext JS Library 1.1.1
35827  * Copyright(c) 2006-2007, Ext JS, LLC.
35828  *
35829  * Originally Released Under LGPL - original licence link has changed is not relivant.
35830  *
35831  * Fork - LGPL
35832  * <script type="text/javascript">
35833  */
35834  
35835
35836 /**
35837  * @class Roo.ReaderLayout
35838  * @extends Roo.BorderLayout
35839  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35840  * center region containing two nested regions (a top one for a list view and one for item preview below),
35841  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35842  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35843  * expedites the setup of the overall layout and regions for this common application style.
35844  * Example:
35845  <pre><code>
35846 var reader = new Roo.ReaderLayout();
35847 var CP = Roo.ContentPanel;  // shortcut for adding
35848
35849 reader.beginUpdate();
35850 reader.add("north", new CP("north", "North"));
35851 reader.add("west", new CP("west", {title: "West"}));
35852 reader.add("east", new CP("east", {title: "East"}));
35853
35854 reader.regions.listView.add(new CP("listView", "List"));
35855 reader.regions.preview.add(new CP("preview", "Preview"));
35856 reader.endUpdate();
35857 </code></pre>
35858 * @constructor
35859 * Create a new ReaderLayout
35860 * @param {Object} config Configuration options
35861 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35862 * document.body if omitted)
35863 */
35864 Roo.ReaderLayout = function(config, renderTo){
35865     var c = config || {size:{}};
35866     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35867         north: c.north !== false ? Roo.apply({
35868             split:false,
35869             initialSize: 32,
35870             titlebar: false
35871         }, c.north) : false,
35872         west: c.west !== false ? Roo.apply({
35873             split:true,
35874             initialSize: 200,
35875             minSize: 175,
35876             maxSize: 400,
35877             titlebar: true,
35878             collapsible: true,
35879             animate: true,
35880             margins:{left:5,right:0,bottom:5,top:5},
35881             cmargins:{left:5,right:5,bottom:5,top:5}
35882         }, c.west) : false,
35883         east: c.east !== false ? Roo.apply({
35884             split:true,
35885             initialSize: 200,
35886             minSize: 175,
35887             maxSize: 400,
35888             titlebar: true,
35889             collapsible: true,
35890             animate: true,
35891             margins:{left:0,right:5,bottom:5,top:5},
35892             cmargins:{left:5,right:5,bottom:5,top:5}
35893         }, c.east) : false,
35894         center: Roo.apply({
35895             tabPosition: 'top',
35896             autoScroll:false,
35897             closeOnTab: true,
35898             titlebar:false,
35899             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35900         }, c.center)
35901     });
35902
35903     this.el.addClass('x-reader');
35904
35905     this.beginUpdate();
35906
35907     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35908         south: c.preview !== false ? Roo.apply({
35909             split:true,
35910             initialSize: 200,
35911             minSize: 100,
35912             autoScroll:true,
35913             collapsible:true,
35914             titlebar: true,
35915             cmargins:{top:5,left:0, right:0, bottom:0}
35916         }, c.preview) : false,
35917         center: Roo.apply({
35918             autoScroll:false,
35919             titlebar:false,
35920             minHeight:200
35921         }, c.listView)
35922     });
35923     this.add('center', new Roo.NestedLayoutPanel(inner,
35924             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35925
35926     this.endUpdate();
35927
35928     this.regions.preview = inner.getRegion('south');
35929     this.regions.listView = inner.getRegion('center');
35930 };
35931
35932 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35933  * Based on:
35934  * Ext JS Library 1.1.1
35935  * Copyright(c) 2006-2007, Ext JS, LLC.
35936  *
35937  * Originally Released Under LGPL - original licence link has changed is not relivant.
35938  *
35939  * Fork - LGPL
35940  * <script type="text/javascript">
35941  */
35942  
35943 /**
35944  * @class Roo.grid.Grid
35945  * @extends Roo.util.Observable
35946  * This class represents the primary interface of a component based grid control.
35947  * <br><br>Usage:<pre><code>
35948  var grid = new Roo.grid.Grid("my-container-id", {
35949      ds: myDataStore,
35950      cm: myColModel,
35951      selModel: mySelectionModel,
35952      autoSizeColumns: true,
35953      monitorWindowResize: false,
35954      trackMouseOver: true
35955  });
35956  // set any options
35957  grid.render();
35958  * </code></pre>
35959  * <b>Common Problems:</b><br/>
35960  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35961  * element will correct this<br/>
35962  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35963  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35964  * are unpredictable.<br/>
35965  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35966  * grid to calculate dimensions/offsets.<br/>
35967   * @constructor
35968  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35969  * The container MUST have some type of size defined for the grid to fill. The container will be
35970  * automatically set to position relative if it isn't already.
35971  * @param {Object} config A config object that sets properties on this grid.
35972  */
35973 Roo.grid.Grid = function(container, config){
35974         // initialize the container
35975         this.container = Roo.get(container);
35976         this.container.update("");
35977         this.container.setStyle("overflow", "hidden");
35978     this.container.addClass('x-grid-container');
35979
35980     this.id = this.container.id;
35981
35982     Roo.apply(this, config);
35983     // check and correct shorthanded configs
35984     if(this.ds){
35985         this.dataSource = this.ds;
35986         delete this.ds;
35987     }
35988     if(this.cm){
35989         this.colModel = this.cm;
35990         delete this.cm;
35991     }
35992     if(this.sm){
35993         this.selModel = this.sm;
35994         delete this.sm;
35995     }
35996
35997     if (this.selModel) {
35998         this.selModel = Roo.factory(this.selModel, Roo.grid);
35999         this.sm = this.selModel;
36000         this.sm.xmodule = this.xmodule || false;
36001     }
36002     if (typeof(this.colModel.config) == 'undefined') {
36003         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36004         this.cm = this.colModel;
36005         this.cm.xmodule = this.xmodule || false;
36006     }
36007     if (this.dataSource) {
36008         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36009         this.ds = this.dataSource;
36010         this.ds.xmodule = this.xmodule || false;
36011          
36012     }
36013     
36014     
36015     
36016     if(this.width){
36017         this.container.setWidth(this.width);
36018     }
36019
36020     if(this.height){
36021         this.container.setHeight(this.height);
36022     }
36023     /** @private */
36024         this.addEvents({
36025         // raw events
36026         /**
36027          * @event click
36028          * The raw click event for the entire grid.
36029          * @param {Roo.EventObject} e
36030          */
36031         "click" : true,
36032         /**
36033          * @event dblclick
36034          * The raw dblclick event for the entire grid.
36035          * @param {Roo.EventObject} e
36036          */
36037         "dblclick" : true,
36038         /**
36039          * @event contextmenu
36040          * The raw contextmenu event for the entire grid.
36041          * @param {Roo.EventObject} e
36042          */
36043         "contextmenu" : true,
36044         /**
36045          * @event mousedown
36046          * The raw mousedown event for the entire grid.
36047          * @param {Roo.EventObject} e
36048          */
36049         "mousedown" : true,
36050         /**
36051          * @event mouseup
36052          * The raw mouseup event for the entire grid.
36053          * @param {Roo.EventObject} e
36054          */
36055         "mouseup" : true,
36056         /**
36057          * @event mouseover
36058          * The raw mouseover event for the entire grid.
36059          * @param {Roo.EventObject} e
36060          */
36061         "mouseover" : true,
36062         /**
36063          * @event mouseout
36064          * The raw mouseout event for the entire grid.
36065          * @param {Roo.EventObject} e
36066          */
36067         "mouseout" : true,
36068         /**
36069          * @event keypress
36070          * The raw keypress event for the entire grid.
36071          * @param {Roo.EventObject} e
36072          */
36073         "keypress" : true,
36074         /**
36075          * @event keydown
36076          * The raw keydown event for the entire grid.
36077          * @param {Roo.EventObject} e
36078          */
36079         "keydown" : true,
36080
36081         // custom events
36082
36083         /**
36084          * @event cellclick
36085          * Fires when a cell is clicked
36086          * @param {Grid} this
36087          * @param {Number} rowIndex
36088          * @param {Number} columnIndex
36089          * @param {Roo.EventObject} e
36090          */
36091         "cellclick" : true,
36092         /**
36093          * @event celldblclick
36094          * Fires when a cell is double clicked
36095          * @param {Grid} this
36096          * @param {Number} rowIndex
36097          * @param {Number} columnIndex
36098          * @param {Roo.EventObject} e
36099          */
36100         "celldblclick" : true,
36101         /**
36102          * @event rowclick
36103          * Fires when a row is clicked
36104          * @param {Grid} this
36105          * @param {Number} rowIndex
36106          * @param {Roo.EventObject} e
36107          */
36108         "rowclick" : true,
36109         /**
36110          * @event rowdblclick
36111          * Fires when a row is double clicked
36112          * @param {Grid} this
36113          * @param {Number} rowIndex
36114          * @param {Roo.EventObject} e
36115          */
36116         "rowdblclick" : true,
36117         /**
36118          * @event headerclick
36119          * Fires when a header is clicked
36120          * @param {Grid} this
36121          * @param {Number} columnIndex
36122          * @param {Roo.EventObject} e
36123          */
36124         "headerclick" : true,
36125         /**
36126          * @event headerdblclick
36127          * Fires when a header cell is double clicked
36128          * @param {Grid} this
36129          * @param {Number} columnIndex
36130          * @param {Roo.EventObject} e
36131          */
36132         "headerdblclick" : true,
36133         /**
36134          * @event rowcontextmenu
36135          * Fires when a row is right clicked
36136          * @param {Grid} this
36137          * @param {Number} rowIndex
36138          * @param {Roo.EventObject} e
36139          */
36140         "rowcontextmenu" : true,
36141         /**
36142          * @event cellcontextmenu
36143          * Fires when a cell is right clicked
36144          * @param {Grid} this
36145          * @param {Number} rowIndex
36146          * @param {Number} cellIndex
36147          * @param {Roo.EventObject} e
36148          */
36149          "cellcontextmenu" : true,
36150         /**
36151          * @event headercontextmenu
36152          * Fires when a header is right clicked
36153          * @param {Grid} this
36154          * @param {Number} columnIndex
36155          * @param {Roo.EventObject} e
36156          */
36157         "headercontextmenu" : true,
36158         /**
36159          * @event bodyscroll
36160          * Fires when the body element is scrolled
36161          * @param {Number} scrollLeft
36162          * @param {Number} scrollTop
36163          */
36164         "bodyscroll" : true,
36165         /**
36166          * @event columnresize
36167          * Fires when the user resizes a column
36168          * @param {Number} columnIndex
36169          * @param {Number} newSize
36170          */
36171         "columnresize" : true,
36172         /**
36173          * @event columnmove
36174          * Fires when the user moves a column
36175          * @param {Number} oldIndex
36176          * @param {Number} newIndex
36177          */
36178         "columnmove" : true,
36179         /**
36180          * @event startdrag
36181          * Fires when row(s) start being dragged
36182          * @param {Grid} this
36183          * @param {Roo.GridDD} dd The drag drop object
36184          * @param {event} e The raw browser event
36185          */
36186         "startdrag" : true,
36187         /**
36188          * @event enddrag
36189          * Fires when a drag operation is complete
36190          * @param {Grid} this
36191          * @param {Roo.GridDD} dd The drag drop object
36192          * @param {event} e The raw browser event
36193          */
36194         "enddrag" : true,
36195         /**
36196          * @event dragdrop
36197          * Fires when dragged row(s) are dropped on a valid DD target
36198          * @param {Grid} this
36199          * @param {Roo.GridDD} dd The drag drop object
36200          * @param {String} targetId The target drag drop object
36201          * @param {event} e The raw browser event
36202          */
36203         "dragdrop" : true,
36204         /**
36205          * @event dragover
36206          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36207          * @param {Grid} this
36208          * @param {Roo.GridDD} dd The drag drop object
36209          * @param {String} targetId The target drag drop object
36210          * @param {event} e The raw browser event
36211          */
36212         "dragover" : true,
36213         /**
36214          * @event dragenter
36215          *  Fires when the dragged row(s) first cross another DD target while being dragged
36216          * @param {Grid} this
36217          * @param {Roo.GridDD} dd The drag drop object
36218          * @param {String} targetId The target drag drop object
36219          * @param {event} e The raw browser event
36220          */
36221         "dragenter" : true,
36222         /**
36223          * @event dragout
36224          * Fires when the dragged row(s) leave another DD target while being dragged
36225          * @param {Grid} this
36226          * @param {Roo.GridDD} dd The drag drop object
36227          * @param {String} targetId The target drag drop object
36228          * @param {event} e The raw browser event
36229          */
36230         "dragout" : true,
36231         /**
36232          * @event rowclass
36233          * Fires when a row is rendered, so you can change add a style to it.
36234          * @param {GridView} gridview   The grid view
36235          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36236          */
36237         'rowclass' : true,
36238
36239         /**
36240          * @event render
36241          * Fires when the grid is rendered
36242          * @param {Grid} grid
36243          */
36244         'render' : true
36245     });
36246
36247     Roo.grid.Grid.superclass.constructor.call(this);
36248 };
36249 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36250     
36251     /**
36252      * @cfg {String} ddGroup - drag drop group.
36253      */
36254
36255     /**
36256      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36257      */
36258     minColumnWidth : 25,
36259
36260     /**
36261      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36262      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36263      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36264      */
36265     autoSizeColumns : false,
36266
36267     /**
36268      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36269      */
36270     autoSizeHeaders : true,
36271
36272     /**
36273      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36274      */
36275     monitorWindowResize : true,
36276
36277     /**
36278      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36279      * rows measured to get a columns size. Default is 0 (all rows).
36280      */
36281     maxRowsToMeasure : 0,
36282
36283     /**
36284      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36285      */
36286     trackMouseOver : true,
36287
36288     /**
36289     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36290     */
36291     
36292     /**
36293     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36294     */
36295     enableDragDrop : false,
36296     
36297     /**
36298     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36299     */
36300     enableColumnMove : true,
36301     
36302     /**
36303     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36304     */
36305     enableColumnHide : true,
36306     
36307     /**
36308     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36309     */
36310     enableRowHeightSync : false,
36311     
36312     /**
36313     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36314     */
36315     stripeRows : true,
36316     
36317     /**
36318     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36319     */
36320     autoHeight : false,
36321
36322     /**
36323      * @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.
36324      */
36325     autoExpandColumn : false,
36326
36327     /**
36328     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36329     * Default is 50.
36330     */
36331     autoExpandMin : 50,
36332
36333     /**
36334     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36335     */
36336     autoExpandMax : 1000,
36337
36338     /**
36339     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36340     */
36341     view : null,
36342
36343     /**
36344     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36345     */
36346     loadMask : false,
36347     /**
36348     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36349     */
36350     dropTarget: false,
36351     
36352    
36353     
36354     // private
36355     rendered : false,
36356
36357     /**
36358     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36359     * of a fixed width. Default is false.
36360     */
36361     /**
36362     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36363     */
36364     /**
36365      * Called once after all setup has been completed and the grid is ready to be rendered.
36366      * @return {Roo.grid.Grid} this
36367      */
36368     render : function()
36369     {
36370         var c = this.container;
36371         // try to detect autoHeight/width mode
36372         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36373             this.autoHeight = true;
36374         }
36375         var view = this.getView();
36376         view.init(this);
36377
36378         c.on("click", this.onClick, this);
36379         c.on("dblclick", this.onDblClick, this);
36380         c.on("contextmenu", this.onContextMenu, this);
36381         c.on("keydown", this.onKeyDown, this);
36382         if (Roo.isTouch) {
36383             c.on("touchstart", this.onTouchStart, this);
36384         }
36385
36386         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36387
36388         this.getSelectionModel().init(this);
36389
36390         view.render();
36391
36392         if(this.loadMask){
36393             this.loadMask = new Roo.LoadMask(this.container,
36394                     Roo.apply({store:this.dataSource}, this.loadMask));
36395         }
36396         
36397         
36398         if (this.toolbar && this.toolbar.xtype) {
36399             this.toolbar.container = this.getView().getHeaderPanel(true);
36400             this.toolbar = new Roo.Toolbar(this.toolbar);
36401         }
36402         if (this.footer && this.footer.xtype) {
36403             this.footer.dataSource = this.getDataSource();
36404             this.footer.container = this.getView().getFooterPanel(true);
36405             this.footer = Roo.factory(this.footer, Roo);
36406         }
36407         if (this.dropTarget && this.dropTarget.xtype) {
36408             delete this.dropTarget.xtype;
36409             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36410         }
36411         
36412         
36413         this.rendered = true;
36414         this.fireEvent('render', this);
36415         return this;
36416     },
36417
36418         /**
36419          * Reconfigures the grid to use a different Store and Column Model.
36420          * The View will be bound to the new objects and refreshed.
36421          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36422          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36423          */
36424     reconfigure : function(dataSource, colModel){
36425         if(this.loadMask){
36426             this.loadMask.destroy();
36427             this.loadMask = new Roo.LoadMask(this.container,
36428                     Roo.apply({store:dataSource}, this.loadMask));
36429         }
36430         this.view.bind(dataSource, colModel);
36431         this.dataSource = dataSource;
36432         this.colModel = colModel;
36433         this.view.refresh(true);
36434     },
36435
36436     // private
36437     onKeyDown : function(e){
36438         this.fireEvent("keydown", e);
36439     },
36440
36441     /**
36442      * Destroy this grid.
36443      * @param {Boolean} removeEl True to remove the element
36444      */
36445     destroy : function(removeEl, keepListeners){
36446         if(this.loadMask){
36447             this.loadMask.destroy();
36448         }
36449         var c = this.container;
36450         c.removeAllListeners();
36451         this.view.destroy();
36452         this.colModel.purgeListeners();
36453         if(!keepListeners){
36454             this.purgeListeners();
36455         }
36456         c.update("");
36457         if(removeEl === true){
36458             c.remove();
36459         }
36460     },
36461
36462     // private
36463     processEvent : function(name, e){
36464         // does this fire select???
36465         //Roo.log('grid:processEvent '  + name);
36466         
36467         if (name != 'touchstart' ) {
36468             this.fireEvent(name, e);    
36469         }
36470         
36471         var t = e.getTarget();
36472         var v = this.view;
36473         var header = v.findHeaderIndex(t);
36474         if(header !== false){
36475             var ename = name == 'touchstart' ? 'click' : name;
36476              
36477             this.fireEvent("header" + ename, this, header, e);
36478         }else{
36479             var row = v.findRowIndex(t);
36480             var cell = v.findCellIndex(t);
36481             if (name == 'touchstart') {
36482                 // first touch is always a click.
36483                 // hopefull this happens after selection is updated.?
36484                 name = false;
36485                 
36486                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36487                     var cs = this.selModel.getSelectedCell();
36488                     if (row == cs[0] && cell == cs[1]){
36489                         name = 'dblclick';
36490                     }
36491                 }
36492                 if (typeof(this.selModel.getSelections) != 'undefined') {
36493                     var cs = this.selModel.getSelections();
36494                     var ds = this.dataSource;
36495                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36496                         name = 'dblclick';
36497                     }
36498                 }
36499                 if (!name) {
36500                     return;
36501                 }
36502             }
36503             
36504             
36505             if(row !== false){
36506                 this.fireEvent("row" + name, this, row, e);
36507                 if(cell !== false){
36508                     this.fireEvent("cell" + name, this, row, cell, e);
36509                 }
36510             }
36511         }
36512     },
36513
36514     // private
36515     onClick : function(e){
36516         this.processEvent("click", e);
36517     },
36518    // private
36519     onTouchStart : function(e){
36520         this.processEvent("touchstart", e);
36521     },
36522
36523     // private
36524     onContextMenu : function(e, t){
36525         this.processEvent("contextmenu", e);
36526     },
36527
36528     // private
36529     onDblClick : function(e){
36530         this.processEvent("dblclick", e);
36531     },
36532
36533     // private
36534     walkCells : function(row, col, step, fn, scope){
36535         var cm = this.colModel, clen = cm.getColumnCount();
36536         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36537         if(step < 0){
36538             if(col < 0){
36539                 row--;
36540                 first = false;
36541             }
36542             while(row >= 0){
36543                 if(!first){
36544                     col = clen-1;
36545                 }
36546                 first = false;
36547                 while(col >= 0){
36548                     if(fn.call(scope || this, row, col, cm) === true){
36549                         return [row, col];
36550                     }
36551                     col--;
36552                 }
36553                 row--;
36554             }
36555         } else {
36556             if(col >= clen){
36557                 row++;
36558                 first = false;
36559             }
36560             while(row < rlen){
36561                 if(!first){
36562                     col = 0;
36563                 }
36564                 first = false;
36565                 while(col < clen){
36566                     if(fn.call(scope || this, row, col, cm) === true){
36567                         return [row, col];
36568                     }
36569                     col++;
36570                 }
36571                 row++;
36572             }
36573         }
36574         return null;
36575     },
36576
36577     // private
36578     getSelections : function(){
36579         return this.selModel.getSelections();
36580     },
36581
36582     /**
36583      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36584      * but if manual update is required this method will initiate it.
36585      */
36586     autoSize : function(){
36587         if(this.rendered){
36588             this.view.layout();
36589             if(this.view.adjustForScroll){
36590                 this.view.adjustForScroll();
36591             }
36592         }
36593     },
36594
36595     /**
36596      * Returns the grid's underlying element.
36597      * @return {Element} The element
36598      */
36599     getGridEl : function(){
36600         return this.container;
36601     },
36602
36603     // private for compatibility, overridden by editor grid
36604     stopEditing : function(){},
36605
36606     /**
36607      * Returns the grid's SelectionModel.
36608      * @return {SelectionModel}
36609      */
36610     getSelectionModel : function(){
36611         if(!this.selModel){
36612             this.selModel = new Roo.grid.RowSelectionModel();
36613         }
36614         return this.selModel;
36615     },
36616
36617     /**
36618      * Returns the grid's DataSource.
36619      * @return {DataSource}
36620      */
36621     getDataSource : function(){
36622         return this.dataSource;
36623     },
36624
36625     /**
36626      * Returns the grid's ColumnModel.
36627      * @return {ColumnModel}
36628      */
36629     getColumnModel : function(){
36630         return this.colModel;
36631     },
36632
36633     /**
36634      * Returns the grid's GridView object.
36635      * @return {GridView}
36636      */
36637     getView : function(){
36638         if(!this.view){
36639             this.view = new Roo.grid.GridView(this.viewConfig);
36640         }
36641         return this.view;
36642     },
36643     /**
36644      * Called to get grid's drag proxy text, by default returns this.ddText.
36645      * @return {String}
36646      */
36647     getDragDropText : function(){
36648         var count = this.selModel.getCount();
36649         return String.format(this.ddText, count, count == 1 ? '' : 's');
36650     }
36651 });
36652 /**
36653  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36654  * %0 is replaced with the number of selected rows.
36655  * @type String
36656  */
36657 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36658  * Based on:
36659  * Ext JS Library 1.1.1
36660  * Copyright(c) 2006-2007, Ext JS, LLC.
36661  *
36662  * Originally Released Under LGPL - original licence link has changed is not relivant.
36663  *
36664  * Fork - LGPL
36665  * <script type="text/javascript">
36666  */
36667  
36668 Roo.grid.AbstractGridView = function(){
36669         this.grid = null;
36670         
36671         this.events = {
36672             "beforerowremoved" : true,
36673             "beforerowsinserted" : true,
36674             "beforerefresh" : true,
36675             "rowremoved" : true,
36676             "rowsinserted" : true,
36677             "rowupdated" : true,
36678             "refresh" : true
36679         };
36680     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36681 };
36682
36683 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36684     rowClass : "x-grid-row",
36685     cellClass : "x-grid-cell",
36686     tdClass : "x-grid-td",
36687     hdClass : "x-grid-hd",
36688     splitClass : "x-grid-hd-split",
36689     
36690     init: function(grid){
36691         this.grid = grid;
36692                 var cid = this.grid.getGridEl().id;
36693         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36694         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36695         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36696         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36697         },
36698         
36699     getColumnRenderers : function(){
36700         var renderers = [];
36701         var cm = this.grid.colModel;
36702         var colCount = cm.getColumnCount();
36703         for(var i = 0; i < colCount; i++){
36704             renderers[i] = cm.getRenderer(i);
36705         }
36706         return renderers;
36707     },
36708     
36709     getColumnIds : function(){
36710         var ids = [];
36711         var cm = this.grid.colModel;
36712         var colCount = cm.getColumnCount();
36713         for(var i = 0; i < colCount; i++){
36714             ids[i] = cm.getColumnId(i);
36715         }
36716         return ids;
36717     },
36718     
36719     getDataIndexes : function(){
36720         if(!this.indexMap){
36721             this.indexMap = this.buildIndexMap();
36722         }
36723         return this.indexMap.colToData;
36724     },
36725     
36726     getColumnIndexByDataIndex : function(dataIndex){
36727         if(!this.indexMap){
36728             this.indexMap = this.buildIndexMap();
36729         }
36730         return this.indexMap.dataToCol[dataIndex];
36731     },
36732     
36733     /**
36734      * Set a css style for a column dynamically. 
36735      * @param {Number} colIndex The index of the column
36736      * @param {String} name The css property name
36737      * @param {String} value The css value
36738      */
36739     setCSSStyle : function(colIndex, name, value){
36740         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36741         Roo.util.CSS.updateRule(selector, name, value);
36742     },
36743     
36744     generateRules : function(cm){
36745         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36746         Roo.util.CSS.removeStyleSheet(rulesId);
36747         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36748             var cid = cm.getColumnId(i);
36749             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36750                          this.tdSelector, cid, " {\n}\n",
36751                          this.hdSelector, cid, " {\n}\n",
36752                          this.splitSelector, cid, " {\n}\n");
36753         }
36754         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36755     }
36756 });/*
36757  * Based on:
36758  * Ext JS Library 1.1.1
36759  * Copyright(c) 2006-2007, Ext JS, LLC.
36760  *
36761  * Originally Released Under LGPL - original licence link has changed is not relivant.
36762  *
36763  * Fork - LGPL
36764  * <script type="text/javascript">
36765  */
36766
36767 // private
36768 // This is a support class used internally by the Grid components
36769 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36770     this.grid = grid;
36771     this.view = grid.getView();
36772     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36773     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36774     if(hd2){
36775         this.setHandleElId(Roo.id(hd));
36776         this.setOuterHandleElId(Roo.id(hd2));
36777     }
36778     this.scroll = false;
36779 };
36780 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36781     maxDragWidth: 120,
36782     getDragData : function(e){
36783         var t = Roo.lib.Event.getTarget(e);
36784         var h = this.view.findHeaderCell(t);
36785         if(h){
36786             return {ddel: h.firstChild, header:h};
36787         }
36788         return false;
36789     },
36790
36791     onInitDrag : function(e){
36792         this.view.headersDisabled = true;
36793         var clone = this.dragData.ddel.cloneNode(true);
36794         clone.id = Roo.id();
36795         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36796         this.proxy.update(clone);
36797         return true;
36798     },
36799
36800     afterValidDrop : function(){
36801         var v = this.view;
36802         setTimeout(function(){
36803             v.headersDisabled = false;
36804         }, 50);
36805     },
36806
36807     afterInvalidDrop : function(){
36808         var v = this.view;
36809         setTimeout(function(){
36810             v.headersDisabled = false;
36811         }, 50);
36812     }
36813 });
36814 /*
36815  * Based on:
36816  * Ext JS Library 1.1.1
36817  * Copyright(c) 2006-2007, Ext JS, LLC.
36818  *
36819  * Originally Released Under LGPL - original licence link has changed is not relivant.
36820  *
36821  * Fork - LGPL
36822  * <script type="text/javascript">
36823  */
36824 // private
36825 // This is a support class used internally by the Grid components
36826 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36827     this.grid = grid;
36828     this.view = grid.getView();
36829     // split the proxies so they don't interfere with mouse events
36830     this.proxyTop = Roo.DomHelper.append(document.body, {
36831         cls:"col-move-top", html:"&#160;"
36832     }, true);
36833     this.proxyBottom = Roo.DomHelper.append(document.body, {
36834         cls:"col-move-bottom", html:"&#160;"
36835     }, true);
36836     this.proxyTop.hide = this.proxyBottom.hide = function(){
36837         this.setLeftTop(-100,-100);
36838         this.setStyle("visibility", "hidden");
36839     };
36840     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36841     // temporarily disabled
36842     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36843     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36844 };
36845 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36846     proxyOffsets : [-4, -9],
36847     fly: Roo.Element.fly,
36848
36849     getTargetFromEvent : function(e){
36850         var t = Roo.lib.Event.getTarget(e);
36851         var cindex = this.view.findCellIndex(t);
36852         if(cindex !== false){
36853             return this.view.getHeaderCell(cindex);
36854         }
36855         return null;
36856     },
36857
36858     nextVisible : function(h){
36859         var v = this.view, cm = this.grid.colModel;
36860         h = h.nextSibling;
36861         while(h){
36862             if(!cm.isHidden(v.getCellIndex(h))){
36863                 return h;
36864             }
36865             h = h.nextSibling;
36866         }
36867         return null;
36868     },
36869
36870     prevVisible : function(h){
36871         var v = this.view, cm = this.grid.colModel;
36872         h = h.prevSibling;
36873         while(h){
36874             if(!cm.isHidden(v.getCellIndex(h))){
36875                 return h;
36876             }
36877             h = h.prevSibling;
36878         }
36879         return null;
36880     },
36881
36882     positionIndicator : function(h, n, e){
36883         var x = Roo.lib.Event.getPageX(e);
36884         var r = Roo.lib.Dom.getRegion(n.firstChild);
36885         var px, pt, py = r.top + this.proxyOffsets[1];
36886         if((r.right - x) <= (r.right-r.left)/2){
36887             px = r.right+this.view.borderWidth;
36888             pt = "after";
36889         }else{
36890             px = r.left;
36891             pt = "before";
36892         }
36893         var oldIndex = this.view.getCellIndex(h);
36894         var newIndex = this.view.getCellIndex(n);
36895
36896         if(this.grid.colModel.isFixed(newIndex)){
36897             return false;
36898         }
36899
36900         var locked = this.grid.colModel.isLocked(newIndex);
36901
36902         if(pt == "after"){
36903             newIndex++;
36904         }
36905         if(oldIndex < newIndex){
36906             newIndex--;
36907         }
36908         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36909             return false;
36910         }
36911         px +=  this.proxyOffsets[0];
36912         this.proxyTop.setLeftTop(px, py);
36913         this.proxyTop.show();
36914         if(!this.bottomOffset){
36915             this.bottomOffset = this.view.mainHd.getHeight();
36916         }
36917         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36918         this.proxyBottom.show();
36919         return pt;
36920     },
36921
36922     onNodeEnter : function(n, dd, e, data){
36923         if(data.header != n){
36924             this.positionIndicator(data.header, n, e);
36925         }
36926     },
36927
36928     onNodeOver : function(n, dd, e, data){
36929         var result = false;
36930         if(data.header != n){
36931             result = this.positionIndicator(data.header, n, e);
36932         }
36933         if(!result){
36934             this.proxyTop.hide();
36935             this.proxyBottom.hide();
36936         }
36937         return result ? this.dropAllowed : this.dropNotAllowed;
36938     },
36939
36940     onNodeOut : function(n, dd, e, data){
36941         this.proxyTop.hide();
36942         this.proxyBottom.hide();
36943     },
36944
36945     onNodeDrop : function(n, dd, e, data){
36946         var h = data.header;
36947         if(h != n){
36948             var cm = this.grid.colModel;
36949             var x = Roo.lib.Event.getPageX(e);
36950             var r = Roo.lib.Dom.getRegion(n.firstChild);
36951             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36952             var oldIndex = this.view.getCellIndex(h);
36953             var newIndex = this.view.getCellIndex(n);
36954             var locked = cm.isLocked(newIndex);
36955             if(pt == "after"){
36956                 newIndex++;
36957             }
36958             if(oldIndex < newIndex){
36959                 newIndex--;
36960             }
36961             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36962                 return false;
36963             }
36964             cm.setLocked(oldIndex, locked, true);
36965             cm.moveColumn(oldIndex, newIndex);
36966             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36967             return true;
36968         }
36969         return false;
36970     }
36971 });
36972 /*
36973  * Based on:
36974  * Ext JS Library 1.1.1
36975  * Copyright(c) 2006-2007, Ext JS, LLC.
36976  *
36977  * Originally Released Under LGPL - original licence link has changed is not relivant.
36978  *
36979  * Fork - LGPL
36980  * <script type="text/javascript">
36981  */
36982   
36983 /**
36984  * @class Roo.grid.GridView
36985  * @extends Roo.util.Observable
36986  *
36987  * @constructor
36988  * @param {Object} config
36989  */
36990 Roo.grid.GridView = function(config){
36991     Roo.grid.GridView.superclass.constructor.call(this);
36992     this.el = null;
36993
36994     Roo.apply(this, config);
36995 };
36996
36997 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36998
36999     unselectable :  'unselectable="on"',
37000     unselectableCls :  'x-unselectable',
37001     
37002     
37003     rowClass : "x-grid-row",
37004
37005     cellClass : "x-grid-col",
37006
37007     tdClass : "x-grid-td",
37008
37009     hdClass : "x-grid-hd",
37010
37011     splitClass : "x-grid-split",
37012
37013     sortClasses : ["sort-asc", "sort-desc"],
37014
37015     enableMoveAnim : false,
37016
37017     hlColor: "C3DAF9",
37018
37019     dh : Roo.DomHelper,
37020
37021     fly : Roo.Element.fly,
37022
37023     css : Roo.util.CSS,
37024
37025     borderWidth: 1,
37026
37027     splitOffset: 3,
37028
37029     scrollIncrement : 22,
37030
37031     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37032
37033     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37034
37035     bind : function(ds, cm){
37036         if(this.ds){
37037             this.ds.un("load", this.onLoad, this);
37038             this.ds.un("datachanged", this.onDataChange, this);
37039             this.ds.un("add", this.onAdd, this);
37040             this.ds.un("remove", this.onRemove, this);
37041             this.ds.un("update", this.onUpdate, this);
37042             this.ds.un("clear", this.onClear, this);
37043         }
37044         if(ds){
37045             ds.on("load", this.onLoad, this);
37046             ds.on("datachanged", this.onDataChange, this);
37047             ds.on("add", this.onAdd, this);
37048             ds.on("remove", this.onRemove, this);
37049             ds.on("update", this.onUpdate, this);
37050             ds.on("clear", this.onClear, this);
37051         }
37052         this.ds = ds;
37053
37054         if(this.cm){
37055             this.cm.un("widthchange", this.onColWidthChange, this);
37056             this.cm.un("headerchange", this.onHeaderChange, this);
37057             this.cm.un("hiddenchange", this.onHiddenChange, this);
37058             this.cm.un("columnmoved", this.onColumnMove, this);
37059             this.cm.un("columnlockchange", this.onColumnLock, this);
37060         }
37061         if(cm){
37062             this.generateRules(cm);
37063             cm.on("widthchange", this.onColWidthChange, this);
37064             cm.on("headerchange", this.onHeaderChange, this);
37065             cm.on("hiddenchange", this.onHiddenChange, this);
37066             cm.on("columnmoved", this.onColumnMove, this);
37067             cm.on("columnlockchange", this.onColumnLock, this);
37068         }
37069         this.cm = cm;
37070     },
37071
37072     init: function(grid){
37073         Roo.grid.GridView.superclass.init.call(this, grid);
37074
37075         this.bind(grid.dataSource, grid.colModel);
37076
37077         grid.on("headerclick", this.handleHeaderClick, this);
37078
37079         if(grid.trackMouseOver){
37080             grid.on("mouseover", this.onRowOver, this);
37081             grid.on("mouseout", this.onRowOut, this);
37082         }
37083         grid.cancelTextSelection = function(){};
37084         this.gridId = grid.id;
37085
37086         var tpls = this.templates || {};
37087
37088         if(!tpls.master){
37089             tpls.master = new Roo.Template(
37090                '<div class="x-grid" hidefocus="true">',
37091                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37092                   '<div class="x-grid-topbar"></div>',
37093                   '<div class="x-grid-scroller"><div></div></div>',
37094                   '<div class="x-grid-locked">',
37095                       '<div class="x-grid-header">{lockedHeader}</div>',
37096                       '<div class="x-grid-body">{lockedBody}</div>',
37097                   "</div>",
37098                   '<div class="x-grid-viewport">',
37099                       '<div class="x-grid-header">{header}</div>',
37100                       '<div class="x-grid-body">{body}</div>',
37101                   "</div>",
37102                   '<div class="x-grid-bottombar"></div>',
37103                  
37104                   '<div class="x-grid-resize-proxy">&#160;</div>',
37105                "</div>"
37106             );
37107             tpls.master.disableformats = true;
37108         }
37109
37110         if(!tpls.header){
37111             tpls.header = new Roo.Template(
37112                '<table border="0" cellspacing="0" cellpadding="0">',
37113                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37114                "</table>{splits}"
37115             );
37116             tpls.header.disableformats = true;
37117         }
37118         tpls.header.compile();
37119
37120         if(!tpls.hcell){
37121             tpls.hcell = new Roo.Template(
37122                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div " title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37123                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37124                 "</div></td>"
37125              );
37126              tpls.hcell.disableFormats = true;
37127         }
37128         tpls.hcell.compile();
37129
37130         if(!tpls.hsplit){
37131             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37132                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37133             tpls.hsplit.disableFormats = true;
37134         }
37135         tpls.hsplit.compile();
37136
37137         if(!tpls.body){
37138             tpls.body = new Roo.Template(
37139                '<table border="0" cellspacing="0" cellpadding="0">',
37140                "<tbody>{rows}</tbody>",
37141                "</table>"
37142             );
37143             tpls.body.disableFormats = true;
37144         }
37145         tpls.body.compile();
37146
37147         if(!tpls.row){
37148             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37149             tpls.row.disableFormats = true;
37150         }
37151         tpls.row.compile();
37152
37153         if(!tpls.cell){
37154             tpls.cell = new Roo.Template(
37155                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37156                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37157                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37158                 "</td>"
37159             );
37160             tpls.cell.disableFormats = true;
37161         }
37162         tpls.cell.compile();
37163
37164         this.templates = tpls;
37165     },
37166
37167     // remap these for backwards compat
37168     onColWidthChange : function(){
37169         this.updateColumns.apply(this, arguments);
37170     },
37171     onHeaderChange : function(){
37172         this.updateHeaders.apply(this, arguments);
37173     }, 
37174     onHiddenChange : function(){
37175         this.handleHiddenChange.apply(this, arguments);
37176     },
37177     onColumnMove : function(){
37178         this.handleColumnMove.apply(this, arguments);
37179     },
37180     onColumnLock : function(){
37181         this.handleLockChange.apply(this, arguments);
37182     },
37183
37184     onDataChange : function(){
37185         this.refresh();
37186         this.updateHeaderSortState();
37187     },
37188
37189     onClear : function(){
37190         this.refresh();
37191     },
37192
37193     onUpdate : function(ds, record){
37194         this.refreshRow(record);
37195     },
37196
37197     refreshRow : function(record){
37198         var ds = this.ds, index;
37199         if(typeof record == 'number'){
37200             index = record;
37201             record = ds.getAt(index);
37202         }else{
37203             index = ds.indexOf(record);
37204         }
37205         this.insertRows(ds, index, index, true);
37206         this.onRemove(ds, record, index+1, true);
37207         this.syncRowHeights(index, index);
37208         this.layout();
37209         this.fireEvent("rowupdated", this, index, record);
37210     },
37211
37212     onAdd : function(ds, records, index){
37213         this.insertRows(ds, index, index + (records.length-1));
37214     },
37215
37216     onRemove : function(ds, record, index, isUpdate){
37217         if(isUpdate !== true){
37218             this.fireEvent("beforerowremoved", this, index, record);
37219         }
37220         var bt = this.getBodyTable(), lt = this.getLockedTable();
37221         if(bt.rows[index]){
37222             bt.firstChild.removeChild(bt.rows[index]);
37223         }
37224         if(lt.rows[index]){
37225             lt.firstChild.removeChild(lt.rows[index]);
37226         }
37227         if(isUpdate !== true){
37228             this.stripeRows(index);
37229             this.syncRowHeights(index, index);
37230             this.layout();
37231             this.fireEvent("rowremoved", this, index, record);
37232         }
37233     },
37234
37235     onLoad : function(){
37236         this.scrollToTop();
37237     },
37238
37239     /**
37240      * Scrolls the grid to the top
37241      */
37242     scrollToTop : function(){
37243         if(this.scroller){
37244             this.scroller.dom.scrollTop = 0;
37245             this.syncScroll();
37246         }
37247     },
37248
37249     /**
37250      * Gets a panel in the header of the grid that can be used for toolbars etc.
37251      * After modifying the contents of this panel a call to grid.autoSize() may be
37252      * required to register any changes in size.
37253      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37254      * @return Roo.Element
37255      */
37256     getHeaderPanel : function(doShow){
37257         if(doShow){
37258             this.headerPanel.show();
37259         }
37260         return this.headerPanel;
37261     },
37262
37263     /**
37264      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37265      * After modifying the contents of this panel a call to grid.autoSize() may be
37266      * required to register any changes in size.
37267      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37268      * @return Roo.Element
37269      */
37270     getFooterPanel : function(doShow){
37271         if(doShow){
37272             this.footerPanel.show();
37273         }
37274         return this.footerPanel;
37275     },
37276
37277     initElements : function(){
37278         var E = Roo.Element;
37279         var el = this.grid.getGridEl().dom.firstChild;
37280         var cs = el.childNodes;
37281
37282         this.el = new E(el);
37283         
37284          this.focusEl = new E(el.firstChild);
37285         this.focusEl.swallowEvent("click", true);
37286         
37287         this.headerPanel = new E(cs[1]);
37288         this.headerPanel.enableDisplayMode("block");
37289
37290         this.scroller = new E(cs[2]);
37291         this.scrollSizer = new E(this.scroller.dom.firstChild);
37292
37293         this.lockedWrap = new E(cs[3]);
37294         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37295         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37296
37297         this.mainWrap = new E(cs[4]);
37298         this.mainHd = new E(this.mainWrap.dom.firstChild);
37299         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37300
37301         this.footerPanel = new E(cs[5]);
37302         this.footerPanel.enableDisplayMode("block");
37303
37304         this.resizeProxy = new E(cs[6]);
37305
37306         this.headerSelector = String.format(
37307            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37308            this.lockedHd.id, this.mainHd.id
37309         );
37310
37311         this.splitterSelector = String.format(
37312            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37313            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37314         );
37315     },
37316     idToCssName : function(s)
37317     {
37318         return s.replace(/[^a-z0-9]+/ig, '-');
37319     },
37320
37321     getHeaderCell : function(index){
37322         return Roo.DomQuery.select(this.headerSelector)[index];
37323     },
37324
37325     getHeaderCellMeasure : function(index){
37326         return this.getHeaderCell(index).firstChild;
37327     },
37328
37329     getHeaderCellText : function(index){
37330         return this.getHeaderCell(index).firstChild.firstChild;
37331     },
37332
37333     getLockedTable : function(){
37334         return this.lockedBody.dom.firstChild;
37335     },
37336
37337     getBodyTable : function(){
37338         return this.mainBody.dom.firstChild;
37339     },
37340
37341     getLockedRow : function(index){
37342         return this.getLockedTable().rows[index];
37343     },
37344
37345     getRow : function(index){
37346         return this.getBodyTable().rows[index];
37347     },
37348
37349     getRowComposite : function(index){
37350         if(!this.rowEl){
37351             this.rowEl = new Roo.CompositeElementLite();
37352         }
37353         var els = [], lrow, mrow;
37354         if(lrow = this.getLockedRow(index)){
37355             els.push(lrow);
37356         }
37357         if(mrow = this.getRow(index)){
37358             els.push(mrow);
37359         }
37360         this.rowEl.elements = els;
37361         return this.rowEl;
37362     },
37363     /**
37364      * Gets the 'td' of the cell
37365      * 
37366      * @param {Integer} rowIndex row to select
37367      * @param {Integer} colIndex column to select
37368      * 
37369      * @return {Object} 
37370      */
37371     getCell : function(rowIndex, colIndex){
37372         var locked = this.cm.getLockedCount();
37373         var source;
37374         if(colIndex < locked){
37375             source = this.lockedBody.dom.firstChild;
37376         }else{
37377             source = this.mainBody.dom.firstChild;
37378             colIndex -= locked;
37379         }
37380         return source.rows[rowIndex].childNodes[colIndex];
37381     },
37382
37383     getCellText : function(rowIndex, colIndex){
37384         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37385     },
37386
37387     getCellBox : function(cell){
37388         var b = this.fly(cell).getBox();
37389         if(Roo.isOpera){ // opera fails to report the Y
37390             b.y = cell.offsetTop + this.mainBody.getY();
37391         }
37392         return b;
37393     },
37394
37395     getCellIndex : function(cell){
37396         var id = String(cell.className).match(this.cellRE);
37397         if(id){
37398             return parseInt(id[1], 10);
37399         }
37400         return 0;
37401     },
37402
37403     findHeaderIndex : function(n){
37404         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37405         return r ? this.getCellIndex(r) : false;
37406     },
37407
37408     findHeaderCell : function(n){
37409         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37410         return r ? r : false;
37411     },
37412
37413     findRowIndex : function(n){
37414         if(!n){
37415             return false;
37416         }
37417         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37418         return r ? r.rowIndex : false;
37419     },
37420
37421     findCellIndex : function(node){
37422         var stop = this.el.dom;
37423         while(node && node != stop){
37424             if(this.findRE.test(node.className)){
37425                 return this.getCellIndex(node);
37426             }
37427             node = node.parentNode;
37428         }
37429         return false;
37430     },
37431
37432     getColumnId : function(index){
37433         return this.cm.getColumnId(index);
37434     },
37435
37436     getSplitters : function()
37437     {
37438         if(this.splitterSelector){
37439            return Roo.DomQuery.select(this.splitterSelector);
37440         }else{
37441             return null;
37442       }
37443     },
37444
37445     getSplitter : function(index){
37446         return this.getSplitters()[index];
37447     },
37448
37449     onRowOver : function(e, t){
37450         var row;
37451         if((row = this.findRowIndex(t)) !== false){
37452             this.getRowComposite(row).addClass("x-grid-row-over");
37453         }
37454     },
37455
37456     onRowOut : function(e, t){
37457         var row;
37458         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37459             this.getRowComposite(row).removeClass("x-grid-row-over");
37460         }
37461     },
37462
37463     renderHeaders : function(){
37464         var cm = this.cm;
37465         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37466         var cb = [], lb = [], sb = [], lsb = [], p = {};
37467         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37468             p.cellId = "x-grid-hd-0-" + i;
37469             p.splitId = "x-grid-csplit-0-" + i;
37470             p.id = cm.getColumnId(i);
37471             p.title = cm.getColumnTooltip(i) || cm.getColumnHeader(i) || "";
37472             p.value = cm.getColumnHeader(i) || "";
37473             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37474             if(!cm.isLocked(i)){
37475                 cb[cb.length] = ct.apply(p);
37476                 sb[sb.length] = st.apply(p);
37477             }else{
37478                 lb[lb.length] = ct.apply(p);
37479                 lsb[lsb.length] = st.apply(p);
37480             }
37481         }
37482         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37483                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37484     },
37485
37486     updateHeaders : function(){
37487         var html = this.renderHeaders();
37488         this.lockedHd.update(html[0]);
37489         this.mainHd.update(html[1]);
37490     },
37491
37492     /**
37493      * Focuses the specified row.
37494      * @param {Number} row The row index
37495      */
37496     focusRow : function(row)
37497     {
37498         //Roo.log('GridView.focusRow');
37499         var x = this.scroller.dom.scrollLeft;
37500         this.focusCell(row, 0, false);
37501         this.scroller.dom.scrollLeft = x;
37502     },
37503
37504     /**
37505      * Focuses the specified cell.
37506      * @param {Number} row The row index
37507      * @param {Number} col The column index
37508      * @param {Boolean} hscroll false to disable horizontal scrolling
37509      */
37510     focusCell : function(row, col, hscroll)
37511     {
37512         //Roo.log('GridView.focusCell');
37513         var el = this.ensureVisible(row, col, hscroll);
37514         this.focusEl.alignTo(el, "tl-tl");
37515         if(Roo.isGecko){
37516             this.focusEl.focus();
37517         }else{
37518             this.focusEl.focus.defer(1, this.focusEl);
37519         }
37520     },
37521
37522     /**
37523      * Scrolls the specified cell into view
37524      * @param {Number} row The row index
37525      * @param {Number} col The column index
37526      * @param {Boolean} hscroll false to disable horizontal scrolling
37527      */
37528     ensureVisible : function(row, col, hscroll)
37529     {
37530         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37531         //return null; //disable for testing.
37532         if(typeof row != "number"){
37533             row = row.rowIndex;
37534         }
37535         if(row < 0 && row >= this.ds.getCount()){
37536             return  null;
37537         }
37538         col = (col !== undefined ? col : 0);
37539         var cm = this.grid.colModel;
37540         while(cm.isHidden(col)){
37541             col++;
37542         }
37543
37544         var el = this.getCell(row, col);
37545         if(!el){
37546             return null;
37547         }
37548         var c = this.scroller.dom;
37549
37550         var ctop = parseInt(el.offsetTop, 10);
37551         var cleft = parseInt(el.offsetLeft, 10);
37552         var cbot = ctop + el.offsetHeight;
37553         var cright = cleft + el.offsetWidth;
37554         
37555         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37556         var stop = parseInt(c.scrollTop, 10);
37557         var sleft = parseInt(c.scrollLeft, 10);
37558         var sbot = stop + ch;
37559         var sright = sleft + c.clientWidth;
37560         /*
37561         Roo.log('GridView.ensureVisible:' +
37562                 ' ctop:' + ctop +
37563                 ' c.clientHeight:' + c.clientHeight +
37564                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37565                 ' stop:' + stop +
37566                 ' cbot:' + cbot +
37567                 ' sbot:' + sbot +
37568                 ' ch:' + ch  
37569                 );
37570         */
37571         if(ctop < stop){
37572              c.scrollTop = ctop;
37573             //Roo.log("set scrolltop to ctop DISABLE?");
37574         }else if(cbot > sbot){
37575             //Roo.log("set scrolltop to cbot-ch");
37576             c.scrollTop = cbot-ch;
37577         }
37578         
37579         if(hscroll !== false){
37580             if(cleft < sleft){
37581                 c.scrollLeft = cleft;
37582             }else if(cright > sright){
37583                 c.scrollLeft = cright-c.clientWidth;
37584             }
37585         }
37586          
37587         return el;
37588     },
37589
37590     updateColumns : function(){
37591         this.grid.stopEditing();
37592         var cm = this.grid.colModel, colIds = this.getColumnIds();
37593         //var totalWidth = cm.getTotalWidth();
37594         var pos = 0;
37595         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37596             //if(cm.isHidden(i)) continue;
37597             var w = cm.getColumnWidth(i);
37598             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37599             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37600         }
37601         this.updateSplitters();
37602     },
37603
37604     generateRules : function(cm){
37605         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37606         Roo.util.CSS.removeStyleSheet(rulesId);
37607         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37608             var cid = cm.getColumnId(i);
37609             var align = '';
37610             if(cm.config[i].align){
37611                 align = 'text-align:'+cm.config[i].align+';';
37612             }
37613             var hidden = '';
37614             if(cm.isHidden(i)){
37615                 hidden = 'display:none;';
37616             }
37617             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37618             ruleBuf.push(
37619                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37620                     this.hdSelector, cid, " {\n", align, width, "}\n",
37621                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37622                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37623         }
37624         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37625     },
37626
37627     updateSplitters : function(){
37628         var cm = this.cm, s = this.getSplitters();
37629         if(s){ // splitters not created yet
37630             var pos = 0, locked = true;
37631             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37632                 if(cm.isHidden(i)) {
37633                     continue;
37634                 }
37635                 var w = cm.getColumnWidth(i); // make sure it's a number
37636                 if(!cm.isLocked(i) && locked){
37637                     pos = 0;
37638                     locked = false;
37639                 }
37640                 pos += w;
37641                 s[i].style.left = (pos-this.splitOffset) + "px";
37642             }
37643         }
37644     },
37645
37646     handleHiddenChange : function(colModel, colIndex, hidden){
37647         if(hidden){
37648             this.hideColumn(colIndex);
37649         }else{
37650             this.unhideColumn(colIndex);
37651         }
37652     },
37653
37654     hideColumn : function(colIndex){
37655         var cid = this.getColumnId(colIndex);
37656         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37657         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37658         if(Roo.isSafari){
37659             this.updateHeaders();
37660         }
37661         this.updateSplitters();
37662         this.layout();
37663     },
37664
37665     unhideColumn : function(colIndex){
37666         var cid = this.getColumnId(colIndex);
37667         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37668         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37669
37670         if(Roo.isSafari){
37671             this.updateHeaders();
37672         }
37673         this.updateSplitters();
37674         this.layout();
37675     },
37676
37677     insertRows : function(dm, firstRow, lastRow, isUpdate){
37678         if(firstRow == 0 && lastRow == dm.getCount()-1){
37679             this.refresh();
37680         }else{
37681             if(!isUpdate){
37682                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37683             }
37684             var s = this.getScrollState();
37685             var markup = this.renderRows(firstRow, lastRow);
37686             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37687             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37688             this.restoreScroll(s);
37689             if(!isUpdate){
37690                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37691                 this.syncRowHeights(firstRow, lastRow);
37692                 this.stripeRows(firstRow);
37693                 this.layout();
37694             }
37695         }
37696     },
37697
37698     bufferRows : function(markup, target, index){
37699         var before = null, trows = target.rows, tbody = target.tBodies[0];
37700         if(index < trows.length){
37701             before = trows[index];
37702         }
37703         var b = document.createElement("div");
37704         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37705         var rows = b.firstChild.rows;
37706         for(var i = 0, len = rows.length; i < len; i++){
37707             if(before){
37708                 tbody.insertBefore(rows[0], before);
37709             }else{
37710                 tbody.appendChild(rows[0]);
37711             }
37712         }
37713         b.innerHTML = "";
37714         b = null;
37715     },
37716
37717     deleteRows : function(dm, firstRow, lastRow){
37718         if(dm.getRowCount()<1){
37719             this.fireEvent("beforerefresh", this);
37720             this.mainBody.update("");
37721             this.lockedBody.update("");
37722             this.fireEvent("refresh", this);
37723         }else{
37724             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37725             var bt = this.getBodyTable();
37726             var tbody = bt.firstChild;
37727             var rows = bt.rows;
37728             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37729                 tbody.removeChild(rows[firstRow]);
37730             }
37731             this.stripeRows(firstRow);
37732             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37733         }
37734     },
37735
37736     updateRows : function(dataSource, firstRow, lastRow){
37737         var s = this.getScrollState();
37738         this.refresh();
37739         this.restoreScroll(s);
37740     },
37741
37742     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37743         if(!noRefresh){
37744            this.refresh();
37745         }
37746         this.updateHeaderSortState();
37747     },
37748
37749     getScrollState : function(){
37750         
37751         var sb = this.scroller.dom;
37752         return {left: sb.scrollLeft, top: sb.scrollTop};
37753     },
37754
37755     stripeRows : function(startRow){
37756         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37757             return;
37758         }
37759         startRow = startRow || 0;
37760         var rows = this.getBodyTable().rows;
37761         var lrows = this.getLockedTable().rows;
37762         var cls = ' x-grid-row-alt ';
37763         for(var i = startRow, len = rows.length; i < len; i++){
37764             var row = rows[i], lrow = lrows[i];
37765             var isAlt = ((i+1) % 2 == 0);
37766             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37767             if(isAlt == hasAlt){
37768                 continue;
37769             }
37770             if(isAlt){
37771                 row.className += " x-grid-row-alt";
37772             }else{
37773                 row.className = row.className.replace("x-grid-row-alt", "");
37774             }
37775             if(lrow){
37776                 lrow.className = row.className;
37777             }
37778         }
37779     },
37780
37781     restoreScroll : function(state){
37782         //Roo.log('GridView.restoreScroll');
37783         var sb = this.scroller.dom;
37784         sb.scrollLeft = state.left;
37785         sb.scrollTop = state.top;
37786         this.syncScroll();
37787     },
37788
37789     syncScroll : function(){
37790         //Roo.log('GridView.syncScroll');
37791         var sb = this.scroller.dom;
37792         var sh = this.mainHd.dom;
37793         var bs = this.mainBody.dom;
37794         var lv = this.lockedBody.dom;
37795         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37796         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37797     },
37798
37799     handleScroll : function(e){
37800         this.syncScroll();
37801         var sb = this.scroller.dom;
37802         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37803         e.stopEvent();
37804     },
37805
37806     handleWheel : function(e){
37807         var d = e.getWheelDelta();
37808         this.scroller.dom.scrollTop -= d*22;
37809         // set this here to prevent jumpy scrolling on large tables
37810         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37811         e.stopEvent();
37812     },
37813
37814     renderRows : function(startRow, endRow){
37815         // pull in all the crap needed to render rows
37816         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37817         var colCount = cm.getColumnCount();
37818
37819         if(ds.getCount() < 1){
37820             return ["", ""];
37821         }
37822
37823         // build a map for all the columns
37824         var cs = [];
37825         for(var i = 0; i < colCount; i++){
37826             var name = cm.getDataIndex(i);
37827             cs[i] = {
37828                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37829                 renderer : cm.getRenderer(i),
37830                 id : cm.getColumnId(i),
37831                 locked : cm.isLocked(i),
37832                 has_editor : cm.isCellEditable(i)
37833             };
37834         }
37835
37836         startRow = startRow || 0;
37837         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37838
37839         // records to render
37840         var rs = ds.getRange(startRow, endRow);
37841
37842         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37843     },
37844
37845     // As much as I hate to duplicate code, this was branched because FireFox really hates
37846     // [].join("") on strings. The performance difference was substantial enough to
37847     // branch this function
37848     doRender : Roo.isGecko ?
37849             function(cs, rs, ds, startRow, colCount, stripe){
37850                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37851                 // buffers
37852                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37853                 
37854                 var hasListener = this.grid.hasListener('rowclass');
37855                 var rowcfg = {};
37856                 for(var j = 0, len = rs.length; j < len; j++){
37857                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37858                     for(var i = 0; i < colCount; i++){
37859                         c = cs[i];
37860                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37861                         p.id = c.id;
37862                         p.css = p.attr = "";
37863                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37864                         if(p.value == undefined || p.value === "") {
37865                             p.value = "&#160;";
37866                         }
37867                         if(c.has_editor){
37868                             p.css += ' x-grid-editable-cell';
37869                         }
37870                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37871                             p.css +=  ' x-grid-dirty-cell';
37872                         }
37873                         var markup = ct.apply(p);
37874                         if(!c.locked){
37875                             cb+= markup;
37876                         }else{
37877                             lcb+= markup;
37878                         }
37879                     }
37880                     var alt = [];
37881                     if(stripe && ((rowIndex+1) % 2 == 0)){
37882                         alt.push("x-grid-row-alt")
37883                     }
37884                     if(r.dirty){
37885                         alt.push(  " x-grid-dirty-row");
37886                     }
37887                     rp.cells = lcb;
37888                     if(this.getRowClass){
37889                         alt.push(this.getRowClass(r, rowIndex));
37890                     }
37891                     if (hasListener) {
37892                         rowcfg = {
37893                              
37894                             record: r,
37895                             rowIndex : rowIndex,
37896                             rowClass : ''
37897                         };
37898                         this.grid.fireEvent('rowclass', this, rowcfg);
37899                         alt.push(rowcfg.rowClass);
37900                     }
37901                     rp.alt = alt.join(" ");
37902                     lbuf+= rt.apply(rp);
37903                     rp.cells = cb;
37904                     buf+=  rt.apply(rp);
37905                 }
37906                 return [lbuf, buf];
37907             } :
37908             function(cs, rs, ds, startRow, colCount, stripe){
37909                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37910                 // buffers
37911                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37912                 var hasListener = this.grid.hasListener('rowclass');
37913  
37914                 var rowcfg = {};
37915                 for(var j = 0, len = rs.length; j < len; j++){
37916                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37917                     for(var i = 0; i < colCount; i++){
37918                         c = cs[i];
37919                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37920                         p.id = c.id;
37921                         p.css = p.attr = "";
37922                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37923                         if(p.value == undefined || p.value === "") {
37924                             p.value = "&#160;";
37925                         }
37926                         //Roo.log(c);
37927                          if(c.has_editor){
37928                             p.css += ' x-grid-editable-cell';
37929                         }
37930                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37931                             p.css += ' x-grid-dirty-cell' 
37932                         }
37933                         
37934                         var markup = ct.apply(p);
37935                         if(!c.locked){
37936                             cb[cb.length] = markup;
37937                         }else{
37938                             lcb[lcb.length] = markup;
37939                         }
37940                     }
37941                     var alt = [];
37942                     if(stripe && ((rowIndex+1) % 2 == 0)){
37943                         alt.push( "x-grid-row-alt");
37944                     }
37945                     if(r.dirty){
37946                         alt.push(" x-grid-dirty-row");
37947                     }
37948                     rp.cells = lcb;
37949                     if(this.getRowClass){
37950                         alt.push( this.getRowClass(r, rowIndex));
37951                     }
37952                     if (hasListener) {
37953                         rowcfg = {
37954                              
37955                             record: r,
37956                             rowIndex : rowIndex,
37957                             rowClass : ''
37958                         };
37959                         this.grid.fireEvent('rowclass', this, rowcfg);
37960                         alt.push(rowcfg.rowClass);
37961                     }
37962                     
37963                     rp.alt = alt.join(" ");
37964                     rp.cells = lcb.join("");
37965                     lbuf[lbuf.length] = rt.apply(rp);
37966                     rp.cells = cb.join("");
37967                     buf[buf.length] =  rt.apply(rp);
37968                 }
37969                 return [lbuf.join(""), buf.join("")];
37970             },
37971
37972     renderBody : function(){
37973         var markup = this.renderRows();
37974         var bt = this.templates.body;
37975         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37976     },
37977
37978     /**
37979      * Refreshes the grid
37980      * @param {Boolean} headersToo
37981      */
37982     refresh : function(headersToo){
37983         this.fireEvent("beforerefresh", this);
37984         this.grid.stopEditing();
37985         var result = this.renderBody();
37986         this.lockedBody.update(result[0]);
37987         this.mainBody.update(result[1]);
37988         if(headersToo === true){
37989             this.updateHeaders();
37990             this.updateColumns();
37991             this.updateSplitters();
37992             this.updateHeaderSortState();
37993         }
37994         this.syncRowHeights();
37995         this.layout();
37996         this.fireEvent("refresh", this);
37997     },
37998
37999     handleColumnMove : function(cm, oldIndex, newIndex){
38000         this.indexMap = null;
38001         var s = this.getScrollState();
38002         this.refresh(true);
38003         this.restoreScroll(s);
38004         this.afterMove(newIndex);
38005     },
38006
38007     afterMove : function(colIndex){
38008         if(this.enableMoveAnim && Roo.enableFx){
38009             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38010         }
38011         // if multisort - fix sortOrder, and reload..
38012         if (this.grid.dataSource.multiSort) {
38013             // the we can call sort again..
38014             var dm = this.grid.dataSource;
38015             var cm = this.grid.colModel;
38016             var so = [];
38017             for(var i = 0; i < cm.config.length; i++ ) {
38018                 
38019                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38020                     continue; // dont' bother, it's not in sort list or being set.
38021                 }
38022                 
38023                 so.push(cm.config[i].dataIndex);
38024             };
38025             dm.sortOrder = so;
38026             dm.load(dm.lastOptions);
38027             
38028             
38029         }
38030         
38031     },
38032
38033     updateCell : function(dm, rowIndex, dataIndex){
38034         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38035         if(typeof colIndex == "undefined"){ // not present in grid
38036             return;
38037         }
38038         var cm = this.grid.colModel;
38039         var cell = this.getCell(rowIndex, colIndex);
38040         var cellText = this.getCellText(rowIndex, colIndex);
38041
38042         var p = {
38043             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38044             id : cm.getColumnId(colIndex),
38045             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38046         };
38047         var renderer = cm.getRenderer(colIndex);
38048         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38049         if(typeof val == "undefined" || val === "") {
38050             val = "&#160;";
38051         }
38052         cellText.innerHTML = val;
38053         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38054         this.syncRowHeights(rowIndex, rowIndex);
38055     },
38056
38057     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38058         var maxWidth = 0;
38059         if(this.grid.autoSizeHeaders){
38060             var h = this.getHeaderCellMeasure(colIndex);
38061             maxWidth = Math.max(maxWidth, h.scrollWidth);
38062         }
38063         var tb, index;
38064         if(this.cm.isLocked(colIndex)){
38065             tb = this.getLockedTable();
38066             index = colIndex;
38067         }else{
38068             tb = this.getBodyTable();
38069             index = colIndex - this.cm.getLockedCount();
38070         }
38071         if(tb && tb.rows){
38072             var rows = tb.rows;
38073             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38074             for(var i = 0; i < stopIndex; i++){
38075                 var cell = rows[i].childNodes[index].firstChild;
38076                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38077             }
38078         }
38079         return maxWidth + /*margin for error in IE*/ 5;
38080     },
38081     /**
38082      * Autofit a column to its content.
38083      * @param {Number} colIndex
38084      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38085      */
38086      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38087          if(this.cm.isHidden(colIndex)){
38088              return; // can't calc a hidden column
38089          }
38090         if(forceMinSize){
38091             var cid = this.cm.getColumnId(colIndex);
38092             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38093            if(this.grid.autoSizeHeaders){
38094                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38095            }
38096         }
38097         var newWidth = this.calcColumnWidth(colIndex);
38098         this.cm.setColumnWidth(colIndex,
38099             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38100         if(!suppressEvent){
38101             this.grid.fireEvent("columnresize", colIndex, newWidth);
38102         }
38103     },
38104
38105     /**
38106      * Autofits all columns to their content and then expands to fit any extra space in the grid
38107      */
38108      autoSizeColumns : function(){
38109         var cm = this.grid.colModel;
38110         var colCount = cm.getColumnCount();
38111         for(var i = 0; i < colCount; i++){
38112             this.autoSizeColumn(i, true, true);
38113         }
38114         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38115             this.fitColumns();
38116         }else{
38117             this.updateColumns();
38118             this.layout();
38119         }
38120     },
38121
38122     /**
38123      * Autofits all columns to the grid's width proportionate with their current size
38124      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38125      */
38126     fitColumns : function(reserveScrollSpace){
38127         var cm = this.grid.colModel;
38128         var colCount = cm.getColumnCount();
38129         var cols = [];
38130         var width = 0;
38131         var i, w;
38132         for (i = 0; i < colCount; i++){
38133             if(!cm.isHidden(i) && !cm.isFixed(i)){
38134                 w = cm.getColumnWidth(i);
38135                 cols.push(i);
38136                 cols.push(w);
38137                 width += w;
38138             }
38139         }
38140         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38141         if(reserveScrollSpace){
38142             avail -= 17;
38143         }
38144         var frac = (avail - cm.getTotalWidth())/width;
38145         while (cols.length){
38146             w = cols.pop();
38147             i = cols.pop();
38148             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38149         }
38150         this.updateColumns();
38151         this.layout();
38152     },
38153
38154     onRowSelect : function(rowIndex){
38155         var row = this.getRowComposite(rowIndex);
38156         row.addClass("x-grid-row-selected");
38157     },
38158
38159     onRowDeselect : function(rowIndex){
38160         var row = this.getRowComposite(rowIndex);
38161         row.removeClass("x-grid-row-selected");
38162     },
38163
38164     onCellSelect : function(row, col){
38165         var cell = this.getCell(row, col);
38166         if(cell){
38167             Roo.fly(cell).addClass("x-grid-cell-selected");
38168         }
38169     },
38170
38171     onCellDeselect : function(row, col){
38172         var cell = this.getCell(row, col);
38173         if(cell){
38174             Roo.fly(cell).removeClass("x-grid-cell-selected");
38175         }
38176     },
38177
38178     updateHeaderSortState : function(){
38179         
38180         // sort state can be single { field: xxx, direction : yyy}
38181         // or   { xxx=>ASC , yyy : DESC ..... }
38182         
38183         var mstate = {};
38184         if (!this.ds.multiSort) { 
38185             var state = this.ds.getSortState();
38186             if(!state){
38187                 return;
38188             }
38189             mstate[state.field] = state.direction;
38190             // FIXME... - this is not used here.. but might be elsewhere..
38191             this.sortState = state;
38192             
38193         } else {
38194             mstate = this.ds.sortToggle;
38195         }
38196         //remove existing sort classes..
38197         
38198         var sc = this.sortClasses;
38199         var hds = this.el.select(this.headerSelector).removeClass(sc);
38200         
38201         for(var f in mstate) {
38202         
38203             var sortColumn = this.cm.findColumnIndex(f);
38204             
38205             if(sortColumn != -1){
38206                 var sortDir = mstate[f];        
38207                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38208             }
38209         }
38210         
38211          
38212         
38213     },
38214
38215
38216     handleHeaderClick : function(g, index,e){
38217         
38218         Roo.log("header click");
38219         
38220         if (Roo.isTouch) {
38221             // touch events on header are handled by context
38222             this.handleHdCtx(g,index,e);
38223             return;
38224         }
38225         
38226         
38227         if(this.headersDisabled){
38228             return;
38229         }
38230         var dm = g.dataSource, cm = g.colModel;
38231         if(!cm.isSortable(index)){
38232             return;
38233         }
38234         g.stopEditing();
38235         
38236         if (dm.multiSort) {
38237             // update the sortOrder
38238             var so = [];
38239             for(var i = 0; i < cm.config.length; i++ ) {
38240                 
38241                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38242                     continue; // dont' bother, it's not in sort list or being set.
38243                 }
38244                 
38245                 so.push(cm.config[i].dataIndex);
38246             };
38247             dm.sortOrder = so;
38248         }
38249         
38250         
38251         dm.sort(cm.getDataIndex(index));
38252     },
38253
38254
38255     destroy : function(){
38256         if(this.colMenu){
38257             this.colMenu.removeAll();
38258             Roo.menu.MenuMgr.unregister(this.colMenu);
38259             this.colMenu.getEl().remove();
38260             delete this.colMenu;
38261         }
38262         if(this.hmenu){
38263             this.hmenu.removeAll();
38264             Roo.menu.MenuMgr.unregister(this.hmenu);
38265             this.hmenu.getEl().remove();
38266             delete this.hmenu;
38267         }
38268         if(this.grid.enableColumnMove){
38269             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38270             if(dds){
38271                 for(var dd in dds){
38272                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38273                         var elid = dds[dd].dragElId;
38274                         dds[dd].unreg();
38275                         Roo.get(elid).remove();
38276                     } else if(dds[dd].config.isTarget){
38277                         dds[dd].proxyTop.remove();
38278                         dds[dd].proxyBottom.remove();
38279                         dds[dd].unreg();
38280                     }
38281                     if(Roo.dd.DDM.locationCache[dd]){
38282                         delete Roo.dd.DDM.locationCache[dd];
38283                     }
38284                 }
38285                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38286             }
38287         }
38288         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38289         this.bind(null, null);
38290         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38291     },
38292
38293     handleLockChange : function(){
38294         this.refresh(true);
38295     },
38296
38297     onDenyColumnLock : function(){
38298
38299     },
38300
38301     onDenyColumnHide : function(){
38302
38303     },
38304
38305     handleHdMenuClick : function(item){
38306         var index = this.hdCtxIndex;
38307         var cm = this.cm, ds = this.ds;
38308         switch(item.id){
38309             case "asc":
38310                 ds.sort(cm.getDataIndex(index), "ASC");
38311                 break;
38312             case "desc":
38313                 ds.sort(cm.getDataIndex(index), "DESC");
38314                 break;
38315             case "lock":
38316                 var lc = cm.getLockedCount();
38317                 if(cm.getColumnCount(true) <= lc+1){
38318                     this.onDenyColumnLock();
38319                     return;
38320                 }
38321                 if(lc != index){
38322                     cm.setLocked(index, true, true);
38323                     cm.moveColumn(index, lc);
38324                     this.grid.fireEvent("columnmove", index, lc);
38325                 }else{
38326                     cm.setLocked(index, true);
38327                 }
38328             break;
38329             case "unlock":
38330                 var lc = cm.getLockedCount();
38331                 if((lc-1) != index){
38332                     cm.setLocked(index, false, true);
38333                     cm.moveColumn(index, lc-1);
38334                     this.grid.fireEvent("columnmove", index, lc-1);
38335                 }else{
38336                     cm.setLocked(index, false);
38337                 }
38338             break;
38339             case 'wider': // used to expand cols on touch..
38340             case 'narrow':
38341                 var cw = cm.getColumnWidth(index);
38342                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38343                 cw = Math.max(0, cw);
38344                 cw = Math.min(cw,4000);
38345                 cm.setColumnWidth(index, cw);
38346                 break;
38347                 
38348             default:
38349                 index = cm.getIndexById(item.id.substr(4));
38350                 if(index != -1){
38351                     if(item.checked && cm.getColumnCount(true) <= 1){
38352                         this.onDenyColumnHide();
38353                         return false;
38354                     }
38355                     cm.setHidden(index, item.checked);
38356                 }
38357         }
38358         return true;
38359     },
38360
38361     beforeColMenuShow : function(){
38362         var cm = this.cm,  colCount = cm.getColumnCount();
38363         this.colMenu.removeAll();
38364         for(var i = 0; i < colCount; i++){
38365             this.colMenu.add(new Roo.menu.CheckItem({
38366                 id: "col-"+cm.getColumnId(i),
38367                 text: cm.getColumnHeader(i),
38368                 checked: !cm.isHidden(i),
38369                 hideOnClick:false
38370             }));
38371         }
38372     },
38373
38374     handleHdCtx : function(g, index, e){
38375         e.stopEvent();
38376         var hd = this.getHeaderCell(index);
38377         this.hdCtxIndex = index;
38378         var ms = this.hmenu.items, cm = this.cm;
38379         ms.get("asc").setDisabled(!cm.isSortable(index));
38380         ms.get("desc").setDisabled(!cm.isSortable(index));
38381         if(this.grid.enableColLock !== false){
38382             ms.get("lock").setDisabled(cm.isLocked(index));
38383             ms.get("unlock").setDisabled(!cm.isLocked(index));
38384         }
38385         this.hmenu.show(hd, "tl-bl");
38386     },
38387
38388     handleHdOver : function(e){
38389         var hd = this.findHeaderCell(e.getTarget());
38390         if(hd && !this.headersDisabled){
38391             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38392                this.fly(hd).addClass("x-grid-hd-over");
38393             }
38394         }
38395     },
38396
38397     handleHdOut : function(e){
38398         var hd = this.findHeaderCell(e.getTarget());
38399         if(hd){
38400             this.fly(hd).removeClass("x-grid-hd-over");
38401         }
38402     },
38403
38404     handleSplitDblClick : function(e, t){
38405         var i = this.getCellIndex(t);
38406         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38407             this.autoSizeColumn(i, true);
38408             this.layout();
38409         }
38410     },
38411
38412     render : function(){
38413
38414         var cm = this.cm;
38415         var colCount = cm.getColumnCount();
38416
38417         if(this.grid.monitorWindowResize === true){
38418             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38419         }
38420         var header = this.renderHeaders();
38421         var body = this.templates.body.apply({rows:""});
38422         var html = this.templates.master.apply({
38423             lockedBody: body,
38424             body: body,
38425             lockedHeader: header[0],
38426             header: header[1]
38427         });
38428
38429         //this.updateColumns();
38430
38431         this.grid.getGridEl().dom.innerHTML = html;
38432
38433         this.initElements();
38434         
38435         // a kludge to fix the random scolling effect in webkit
38436         this.el.on("scroll", function() {
38437             this.el.dom.scrollTop=0; // hopefully not recursive..
38438         },this);
38439
38440         this.scroller.on("scroll", this.handleScroll, this);
38441         this.lockedBody.on("mousewheel", this.handleWheel, this);
38442         this.mainBody.on("mousewheel", this.handleWheel, this);
38443
38444         this.mainHd.on("mouseover", this.handleHdOver, this);
38445         this.mainHd.on("mouseout", this.handleHdOut, this);
38446         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38447                 {delegate: "."+this.splitClass});
38448
38449         this.lockedHd.on("mouseover", this.handleHdOver, this);
38450         this.lockedHd.on("mouseout", this.handleHdOut, this);
38451         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38452                 {delegate: "."+this.splitClass});
38453
38454         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38455             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38456         }
38457
38458         this.updateSplitters();
38459
38460         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38461             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38462             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38463         }
38464
38465         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38466             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38467             this.hmenu.add(
38468                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38469                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38470             );
38471             if(this.grid.enableColLock !== false){
38472                 this.hmenu.add('-',
38473                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38474                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38475                 );
38476             }
38477             if (Roo.isTouch) {
38478                  this.hmenu.add('-',
38479                     {id:"wider", text: this.columnsWiderText},
38480                     {id:"narrow", text: this.columnsNarrowText }
38481                 );
38482                 
38483                  
38484             }
38485             
38486             if(this.grid.enableColumnHide !== false){
38487
38488                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38489                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38490                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38491
38492                 this.hmenu.add('-',
38493                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38494                 );
38495             }
38496             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38497
38498             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38499         }
38500
38501         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38502             this.dd = new Roo.grid.GridDragZone(this.grid, {
38503                 ddGroup : this.grid.ddGroup || 'GridDD'
38504             });
38505             
38506         }
38507
38508         /*
38509         for(var i = 0; i < colCount; i++){
38510             if(cm.isHidden(i)){
38511                 this.hideColumn(i);
38512             }
38513             if(cm.config[i].align){
38514                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38515                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38516             }
38517         }*/
38518         
38519         this.updateHeaderSortState();
38520
38521         this.beforeInitialResize();
38522         this.layout(true);
38523
38524         // two part rendering gives faster view to the user
38525         this.renderPhase2.defer(1, this);
38526     },
38527
38528     renderPhase2 : function(){
38529         // render the rows now
38530         this.refresh();
38531         if(this.grid.autoSizeColumns){
38532             this.autoSizeColumns();
38533         }
38534     },
38535
38536     beforeInitialResize : function(){
38537
38538     },
38539
38540     onColumnSplitterMoved : function(i, w){
38541         this.userResized = true;
38542         var cm = this.grid.colModel;
38543         cm.setColumnWidth(i, w, true);
38544         var cid = cm.getColumnId(i);
38545         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38546         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38547         this.updateSplitters();
38548         this.layout();
38549         this.grid.fireEvent("columnresize", i, w);
38550     },
38551
38552     syncRowHeights : function(startIndex, endIndex){
38553         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38554             startIndex = startIndex || 0;
38555             var mrows = this.getBodyTable().rows;
38556             var lrows = this.getLockedTable().rows;
38557             var len = mrows.length-1;
38558             endIndex = Math.min(endIndex || len, len);
38559             for(var i = startIndex; i <= endIndex; i++){
38560                 var m = mrows[i], l = lrows[i];
38561                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38562                 m.style.height = l.style.height = h + "px";
38563             }
38564         }
38565     },
38566
38567     layout : function(initialRender, is2ndPass){
38568         var g = this.grid;
38569         var auto = g.autoHeight;
38570         var scrollOffset = 16;
38571         var c = g.getGridEl(), cm = this.cm,
38572                 expandCol = g.autoExpandColumn,
38573                 gv = this;
38574         //c.beginMeasure();
38575
38576         if(!c.dom.offsetWidth){ // display:none?
38577             if(initialRender){
38578                 this.lockedWrap.show();
38579                 this.mainWrap.show();
38580             }
38581             return;
38582         }
38583
38584         var hasLock = this.cm.isLocked(0);
38585
38586         var tbh = this.headerPanel.getHeight();
38587         var bbh = this.footerPanel.getHeight();
38588
38589         if(auto){
38590             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38591             var newHeight = ch + c.getBorderWidth("tb");
38592             if(g.maxHeight){
38593                 newHeight = Math.min(g.maxHeight, newHeight);
38594             }
38595             c.setHeight(newHeight);
38596         }
38597
38598         if(g.autoWidth){
38599             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38600         }
38601
38602         var s = this.scroller;
38603
38604         var csize = c.getSize(true);
38605
38606         this.el.setSize(csize.width, csize.height);
38607
38608         this.headerPanel.setWidth(csize.width);
38609         this.footerPanel.setWidth(csize.width);
38610
38611         var hdHeight = this.mainHd.getHeight();
38612         var vw = csize.width;
38613         var vh = csize.height - (tbh + bbh);
38614
38615         s.setSize(vw, vh);
38616
38617         var bt = this.getBodyTable();
38618         var ltWidth = hasLock ?
38619                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38620
38621         var scrollHeight = bt.offsetHeight;
38622         var scrollWidth = ltWidth + bt.offsetWidth;
38623         var vscroll = false, hscroll = false;
38624
38625         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38626
38627         var lw = this.lockedWrap, mw = this.mainWrap;
38628         var lb = this.lockedBody, mb = this.mainBody;
38629
38630         setTimeout(function(){
38631             var t = s.dom.offsetTop;
38632             var w = s.dom.clientWidth,
38633                 h = s.dom.clientHeight;
38634
38635             lw.setTop(t);
38636             lw.setSize(ltWidth, h);
38637
38638             mw.setLeftTop(ltWidth, t);
38639             mw.setSize(w-ltWidth, h);
38640
38641             lb.setHeight(h-hdHeight);
38642             mb.setHeight(h-hdHeight);
38643
38644             if(is2ndPass !== true && !gv.userResized && expandCol){
38645                 // high speed resize without full column calculation
38646                 
38647                 var ci = cm.getIndexById(expandCol);
38648                 if (ci < 0) {
38649                     ci = cm.findColumnIndex(expandCol);
38650                 }
38651                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38652                 var expandId = cm.getColumnId(ci);
38653                 var  tw = cm.getTotalWidth(false);
38654                 var currentWidth = cm.getColumnWidth(ci);
38655                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38656                 if(currentWidth != cw){
38657                     cm.setColumnWidth(ci, cw, true);
38658                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38659                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38660                     gv.updateSplitters();
38661                     gv.layout(false, true);
38662                 }
38663             }
38664
38665             if(initialRender){
38666                 lw.show();
38667                 mw.show();
38668             }
38669             //c.endMeasure();
38670         }, 10);
38671     },
38672
38673     onWindowResize : function(){
38674         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38675             return;
38676         }
38677         this.layout();
38678     },
38679
38680     appendFooter : function(parentEl){
38681         return null;
38682     },
38683
38684     sortAscText : "Sort Ascending",
38685     sortDescText : "Sort Descending",
38686     lockText : "Lock Column",
38687     unlockText : "Unlock Column",
38688     columnsText : "Columns",
38689  
38690     columnsWiderText : "Wider",
38691     columnsNarrowText : "Thinner"
38692 });
38693
38694
38695 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38696     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38697     this.proxy.el.addClass('x-grid3-col-dd');
38698 };
38699
38700 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38701     handleMouseDown : function(e){
38702
38703     },
38704
38705     callHandleMouseDown : function(e){
38706         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38707     }
38708 });
38709 /*
38710  * Based on:
38711  * Ext JS Library 1.1.1
38712  * Copyright(c) 2006-2007, Ext JS, LLC.
38713  *
38714  * Originally Released Under LGPL - original licence link has changed is not relivant.
38715  *
38716  * Fork - LGPL
38717  * <script type="text/javascript">
38718  */
38719  
38720 // private
38721 // This is a support class used internally by the Grid components
38722 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38723     this.grid = grid;
38724     this.view = grid.getView();
38725     this.proxy = this.view.resizeProxy;
38726     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38727         "gridSplitters" + this.grid.getGridEl().id, {
38728         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38729     });
38730     this.setHandleElId(Roo.id(hd));
38731     this.setOuterHandleElId(Roo.id(hd2));
38732     this.scroll = false;
38733 };
38734 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38735     fly: Roo.Element.fly,
38736
38737     b4StartDrag : function(x, y){
38738         this.view.headersDisabled = true;
38739         this.proxy.setHeight(this.view.mainWrap.getHeight());
38740         var w = this.cm.getColumnWidth(this.cellIndex);
38741         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38742         this.resetConstraints();
38743         this.setXConstraint(minw, 1000);
38744         this.setYConstraint(0, 0);
38745         this.minX = x - minw;
38746         this.maxX = x + 1000;
38747         this.startPos = x;
38748         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38749     },
38750
38751
38752     handleMouseDown : function(e){
38753         ev = Roo.EventObject.setEvent(e);
38754         var t = this.fly(ev.getTarget());
38755         if(t.hasClass("x-grid-split")){
38756             this.cellIndex = this.view.getCellIndex(t.dom);
38757             this.split = t.dom;
38758             this.cm = this.grid.colModel;
38759             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38760                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38761             }
38762         }
38763     },
38764
38765     endDrag : function(e){
38766         this.view.headersDisabled = false;
38767         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38768         var diff = endX - this.startPos;
38769         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38770     },
38771
38772     autoOffset : function(){
38773         this.setDelta(0,0);
38774     }
38775 });/*
38776  * Based on:
38777  * Ext JS Library 1.1.1
38778  * Copyright(c) 2006-2007, Ext JS, LLC.
38779  *
38780  * Originally Released Under LGPL - original licence link has changed is not relivant.
38781  *
38782  * Fork - LGPL
38783  * <script type="text/javascript">
38784  */
38785  
38786 // private
38787 // This is a support class used internally by the Grid components
38788 Roo.grid.GridDragZone = function(grid, config){
38789     this.view = grid.getView();
38790     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38791     if(this.view.lockedBody){
38792         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38793         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38794     }
38795     this.scroll = false;
38796     this.grid = grid;
38797     this.ddel = document.createElement('div');
38798     this.ddel.className = 'x-grid-dd-wrap';
38799 };
38800
38801 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38802     ddGroup : "GridDD",
38803
38804     getDragData : function(e){
38805         var t = Roo.lib.Event.getTarget(e);
38806         var rowIndex = this.view.findRowIndex(t);
38807         var sm = this.grid.selModel;
38808             
38809         //Roo.log(rowIndex);
38810         
38811         if (sm.getSelectedCell) {
38812             // cell selection..
38813             if (!sm.getSelectedCell()) {
38814                 return false;
38815             }
38816             if (rowIndex != sm.getSelectedCell()[0]) {
38817                 return false;
38818             }
38819         
38820         }
38821         
38822         if(rowIndex !== false){
38823             
38824             // if editorgrid.. 
38825             
38826             
38827             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38828                
38829             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38830               //  
38831             //}
38832             if (e.hasModifier()){
38833                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38834             }
38835             
38836             Roo.log("getDragData");
38837             
38838             return {
38839                 grid: this.grid,
38840                 ddel: this.ddel,
38841                 rowIndex: rowIndex,
38842                 selections:sm.getSelections ? sm.getSelections() : (
38843                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38844                 )
38845             };
38846         }
38847         return false;
38848     },
38849
38850     onInitDrag : function(e){
38851         var data = this.dragData;
38852         this.ddel.innerHTML = this.grid.getDragDropText();
38853         this.proxy.update(this.ddel);
38854         // fire start drag?
38855     },
38856
38857     afterRepair : function(){
38858         this.dragging = false;
38859     },
38860
38861     getRepairXY : function(e, data){
38862         return false;
38863     },
38864
38865     onEndDrag : function(data, e){
38866         // fire end drag?
38867     },
38868
38869     onValidDrop : function(dd, e, id){
38870         // fire drag drop?
38871         this.hideProxy();
38872     },
38873
38874     beforeInvalidDrop : function(e, id){
38875
38876     }
38877 });/*
38878  * Based on:
38879  * Ext JS Library 1.1.1
38880  * Copyright(c) 2006-2007, Ext JS, LLC.
38881  *
38882  * Originally Released Under LGPL - original licence link has changed is not relivant.
38883  *
38884  * Fork - LGPL
38885  * <script type="text/javascript">
38886  */
38887  
38888
38889 /**
38890  * @class Roo.grid.ColumnModel
38891  * @extends Roo.util.Observable
38892  * This is the default implementation of a ColumnModel used by the Grid. It defines
38893  * the columns in the grid.
38894  * <br>Usage:<br>
38895  <pre><code>
38896  var colModel = new Roo.grid.ColumnModel([
38897         {header: "Ticker", width: 60, sortable: true, locked: true},
38898         {header: "Company Name", width: 150, sortable: true},
38899         {header: "Market Cap.", width: 100, sortable: true},
38900         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38901         {header: "Employees", width: 100, sortable: true, resizable: false}
38902  ]);
38903  </code></pre>
38904  * <p>
38905  
38906  * The config options listed for this class are options which may appear in each
38907  * individual column definition.
38908  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38909  * @constructor
38910  * @param {Object} config An Array of column config objects. See this class's
38911  * config objects for details.
38912 */
38913 Roo.grid.ColumnModel = function(config){
38914         /**
38915      * The config passed into the constructor
38916      */
38917     this.config = config;
38918     this.lookup = {};
38919
38920     // if no id, create one
38921     // if the column does not have a dataIndex mapping,
38922     // map it to the order it is in the config
38923     for(var i = 0, len = config.length; i < len; i++){
38924         var c = config[i];
38925         if(typeof c.dataIndex == "undefined"){
38926             c.dataIndex = i;
38927         }
38928         if(typeof c.renderer == "string"){
38929             c.renderer = Roo.util.Format[c.renderer];
38930         }
38931         if(typeof c.id == "undefined"){
38932             c.id = Roo.id();
38933         }
38934         if(c.editor && c.editor.xtype){
38935             c.editor  = Roo.factory(c.editor, Roo.grid);
38936         }
38937         if(c.editor && c.editor.isFormField){
38938             c.editor = new Roo.grid.GridEditor(c.editor);
38939         }
38940         this.lookup[c.id] = c;
38941     }
38942
38943     /**
38944      * The width of columns which have no width specified (defaults to 100)
38945      * @type Number
38946      */
38947     this.defaultWidth = 100;
38948
38949     /**
38950      * Default sortable of columns which have no sortable specified (defaults to false)
38951      * @type Boolean
38952      */
38953     this.defaultSortable = false;
38954
38955     this.addEvents({
38956         /**
38957              * @event widthchange
38958              * Fires when the width of a column changes.
38959              * @param {ColumnModel} this
38960              * @param {Number} columnIndex The column index
38961              * @param {Number} newWidth The new width
38962              */
38963             "widthchange": true,
38964         /**
38965              * @event headerchange
38966              * Fires when the text of a header changes.
38967              * @param {ColumnModel} this
38968              * @param {Number} columnIndex The column index
38969              * @param {Number} newText The new header text
38970              */
38971             "headerchange": true,
38972         /**
38973              * @event hiddenchange
38974              * Fires when a column is hidden or "unhidden".
38975              * @param {ColumnModel} this
38976              * @param {Number} columnIndex The column index
38977              * @param {Boolean} hidden true if hidden, false otherwise
38978              */
38979             "hiddenchange": true,
38980             /**
38981          * @event columnmoved
38982          * Fires when a column is moved.
38983          * @param {ColumnModel} this
38984          * @param {Number} oldIndex
38985          * @param {Number} newIndex
38986          */
38987         "columnmoved" : true,
38988         /**
38989          * @event columlockchange
38990          * Fires when a column's locked state is changed
38991          * @param {ColumnModel} this
38992          * @param {Number} colIndex
38993          * @param {Boolean} locked true if locked
38994          */
38995         "columnlockchange" : true
38996     });
38997     Roo.grid.ColumnModel.superclass.constructor.call(this);
38998 };
38999 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39000     /**
39001      * @cfg {String} header The header text to display in the Grid view.
39002      */
39003     /**
39004      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39005      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39006      * specified, the column's index is used as an index into the Record's data Array.
39007      */
39008     /**
39009      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39010      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39011      */
39012     /**
39013      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39014      * Defaults to the value of the {@link #defaultSortable} property.
39015      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39016      */
39017     /**
39018      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39019      */
39020     /**
39021      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39022      */
39023     /**
39024      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39025      */
39026     /**
39027      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39028      */
39029     /**
39030      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39031      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39032      * default renderer uses the raw data value. If an object is returned (bootstrap only)
39033      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39034      */
39035        /**
39036      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39037      */
39038     /**
39039      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39040      */
39041     /**
39042      * @cfg {String} cursor (Optional)
39043      */
39044     /**
39045      * @cfg {String} tooltip (Optional)
39046      */
39047     /**
39048      * @cfg {Number} xs (Optional)
39049      */
39050     /**
39051      * @cfg {Number} sm (Optional)
39052      */
39053     /**
39054      * @cfg {Number} md (Optional)
39055      */
39056     /**
39057      * @cfg {Number} lg (Optional)
39058      */
39059     /**
39060      * Returns the id of the column at the specified index.
39061      * @param {Number} index The column index
39062      * @return {String} the id
39063      */
39064     getColumnId : function(index){
39065         return this.config[index].id;
39066     },
39067
39068     /**
39069      * Returns the column for a specified id.
39070      * @param {String} id The column id
39071      * @return {Object} the column
39072      */
39073     getColumnById : function(id){
39074         return this.lookup[id];
39075     },
39076
39077     
39078     /**
39079      * Returns the column for a specified dataIndex.
39080      * @param {String} dataIndex The column dataIndex
39081      * @return {Object|Boolean} the column or false if not found
39082      */
39083     getColumnByDataIndex: function(dataIndex){
39084         var index = this.findColumnIndex(dataIndex);
39085         return index > -1 ? this.config[index] : false;
39086     },
39087     
39088     /**
39089      * Returns the index for a specified column id.
39090      * @param {String} id The column id
39091      * @return {Number} the index, or -1 if not found
39092      */
39093     getIndexById : function(id){
39094         for(var i = 0, len = this.config.length; i < len; i++){
39095             if(this.config[i].id == id){
39096                 return i;
39097             }
39098         }
39099         return -1;
39100     },
39101     
39102     /**
39103      * Returns the index for a specified column dataIndex.
39104      * @param {String} dataIndex The column dataIndex
39105      * @return {Number} the index, or -1 if not found
39106      */
39107     
39108     findColumnIndex : function(dataIndex){
39109         for(var i = 0, len = this.config.length; i < len; i++){
39110             if(this.config[i].dataIndex == dataIndex){
39111                 return i;
39112             }
39113         }
39114         return -1;
39115     },
39116     
39117     
39118     moveColumn : function(oldIndex, newIndex){
39119         var c = this.config[oldIndex];
39120         this.config.splice(oldIndex, 1);
39121         this.config.splice(newIndex, 0, c);
39122         this.dataMap = null;
39123         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39124     },
39125
39126     isLocked : function(colIndex){
39127         return this.config[colIndex].locked === true;
39128     },
39129
39130     setLocked : function(colIndex, value, suppressEvent){
39131         if(this.isLocked(colIndex) == value){
39132             return;
39133         }
39134         this.config[colIndex].locked = value;
39135         if(!suppressEvent){
39136             this.fireEvent("columnlockchange", this, colIndex, value);
39137         }
39138     },
39139
39140     getTotalLockedWidth : function(){
39141         var totalWidth = 0;
39142         for(var i = 0; i < this.config.length; i++){
39143             if(this.isLocked(i) && !this.isHidden(i)){
39144                 this.totalWidth += this.getColumnWidth(i);
39145             }
39146         }
39147         return totalWidth;
39148     },
39149
39150     getLockedCount : function(){
39151         for(var i = 0, len = this.config.length; i < len; i++){
39152             if(!this.isLocked(i)){
39153                 return i;
39154             }
39155         }
39156     },
39157
39158     /**
39159      * Returns the number of columns.
39160      * @return {Number}
39161      */
39162     getColumnCount : function(visibleOnly){
39163         if(visibleOnly === true){
39164             var c = 0;
39165             for(var i = 0, len = this.config.length; i < len; i++){
39166                 if(!this.isHidden(i)){
39167                     c++;
39168                 }
39169             }
39170             return c;
39171         }
39172         return this.config.length;
39173     },
39174
39175     /**
39176      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39177      * @param {Function} fn
39178      * @param {Object} scope (optional)
39179      * @return {Array} result
39180      */
39181     getColumnsBy : function(fn, scope){
39182         var r = [];
39183         for(var i = 0, len = this.config.length; i < len; i++){
39184             var c = this.config[i];
39185             if(fn.call(scope||this, c, i) === true){
39186                 r[r.length] = c;
39187             }
39188         }
39189         return r;
39190     },
39191
39192     /**
39193      * Returns true if the specified column is sortable.
39194      * @param {Number} col The column index
39195      * @return {Boolean}
39196      */
39197     isSortable : function(col){
39198         if(typeof this.config[col].sortable == "undefined"){
39199             return this.defaultSortable;
39200         }
39201         return this.config[col].sortable;
39202     },
39203
39204     /**
39205      * Returns the rendering (formatting) function defined for the column.
39206      * @param {Number} col The column index.
39207      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39208      */
39209     getRenderer : function(col){
39210         if(!this.config[col].renderer){
39211             return Roo.grid.ColumnModel.defaultRenderer;
39212         }
39213         return this.config[col].renderer;
39214     },
39215
39216     /**
39217      * Sets the rendering (formatting) function for a column.
39218      * @param {Number} col The column index
39219      * @param {Function} fn The function to use to process the cell's raw data
39220      * to return HTML markup for the grid view. The render function is called with
39221      * the following parameters:<ul>
39222      * <li>Data value.</li>
39223      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39224      * <li>css A CSS style string to apply to the table cell.</li>
39225      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39226      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39227      * <li>Row index</li>
39228      * <li>Column index</li>
39229      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39230      */
39231     setRenderer : function(col, fn){
39232         this.config[col].renderer = fn;
39233     },
39234
39235     /**
39236      * Returns the width for the specified column.
39237      * @param {Number} col The column index
39238      * @return {Number}
39239      */
39240     getColumnWidth : function(col){
39241         return this.config[col].width * 1 || this.defaultWidth;
39242     },
39243
39244     /**
39245      * Sets the width for a column.
39246      * @param {Number} col The column index
39247      * @param {Number} width The new width
39248      */
39249     setColumnWidth : function(col, width, suppressEvent){
39250         this.config[col].width = width;
39251         this.totalWidth = null;
39252         if(!suppressEvent){
39253              this.fireEvent("widthchange", this, col, width);
39254         }
39255     },
39256
39257     /**
39258      * Returns the total width of all columns.
39259      * @param {Boolean} includeHidden True to include hidden column widths
39260      * @return {Number}
39261      */
39262     getTotalWidth : function(includeHidden){
39263         if(!this.totalWidth){
39264             this.totalWidth = 0;
39265             for(var i = 0, len = this.config.length; i < len; i++){
39266                 if(includeHidden || !this.isHidden(i)){
39267                     this.totalWidth += this.getColumnWidth(i);
39268                 }
39269             }
39270         }
39271         return this.totalWidth;
39272     },
39273
39274     /**
39275      * Returns the header for the specified column.
39276      * @param {Number} col The column index
39277      * @return {String}
39278      */
39279     getColumnHeader : function(col){
39280         return this.config[col].header;
39281     },
39282
39283     /**
39284      * Sets the header for a column.
39285      * @param {Number} col The column index
39286      * @param {String} header The new header
39287      */
39288     setColumnHeader : function(col, header){
39289         this.config[col].header = header;
39290         this.fireEvent("headerchange", this, col, header);
39291     },
39292
39293     /**
39294      * Returns the tooltip for the specified column.
39295      * @param {Number} col The column index
39296      * @return {String}
39297      */
39298     getColumnTooltip : function(col){
39299             return this.config[col].tooltip;
39300     },
39301     /**
39302      * Sets the tooltip for a column.
39303      * @param {Number} col The column index
39304      * @param {String} tooltip The new tooltip
39305      */
39306     setColumnTooltip : function(col, tooltip){
39307             this.config[col].tooltip = tooltip;
39308     },
39309
39310     /**
39311      * Returns the dataIndex for the specified column.
39312      * @param {Number} col The column index
39313      * @return {Number}
39314      */
39315     getDataIndex : function(col){
39316         return this.config[col].dataIndex;
39317     },
39318
39319     /**
39320      * Sets the dataIndex for a column.
39321      * @param {Number} col The column index
39322      * @param {Number} dataIndex The new dataIndex
39323      */
39324     setDataIndex : function(col, dataIndex){
39325         this.config[col].dataIndex = dataIndex;
39326     },
39327
39328     
39329     
39330     /**
39331      * Returns true if the cell is editable.
39332      * @param {Number} colIndex The column index
39333      * @param {Number} rowIndex The row index - this is nto actually used..?
39334      * @return {Boolean}
39335      */
39336     isCellEditable : function(colIndex, rowIndex){
39337         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39338     },
39339
39340     /**
39341      * Returns the editor defined for the cell/column.
39342      * return false or null to disable editing.
39343      * @param {Number} colIndex The column index
39344      * @param {Number} rowIndex The row index
39345      * @return {Object}
39346      */
39347     getCellEditor : function(colIndex, rowIndex){
39348         return this.config[colIndex].editor;
39349     },
39350
39351     /**
39352      * Sets if a column is editable.
39353      * @param {Number} col The column index
39354      * @param {Boolean} editable True if the column is editable
39355      */
39356     setEditable : function(col, editable){
39357         this.config[col].editable = editable;
39358     },
39359
39360
39361     /**
39362      * Returns true if the column is hidden.
39363      * @param {Number} colIndex The column index
39364      * @return {Boolean}
39365      */
39366     isHidden : function(colIndex){
39367         return this.config[colIndex].hidden;
39368     },
39369
39370
39371     /**
39372      * Returns true if the column width cannot be changed
39373      */
39374     isFixed : function(colIndex){
39375         return this.config[colIndex].fixed;
39376     },
39377
39378     /**
39379      * Returns true if the column can be resized
39380      * @return {Boolean}
39381      */
39382     isResizable : function(colIndex){
39383         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39384     },
39385     /**
39386      * Sets if a column is hidden.
39387      * @param {Number} colIndex The column index
39388      * @param {Boolean} hidden True if the column is hidden
39389      */
39390     setHidden : function(colIndex, hidden){
39391         this.config[colIndex].hidden = hidden;
39392         this.totalWidth = null;
39393         this.fireEvent("hiddenchange", this, colIndex, hidden);
39394     },
39395
39396     /**
39397      * Sets the editor for a column.
39398      * @param {Number} col The column index
39399      * @param {Object} editor The editor object
39400      */
39401     setEditor : function(col, editor){
39402         this.config[col].editor = editor;
39403     }
39404 });
39405
39406 Roo.grid.ColumnModel.defaultRenderer = function(value){
39407         if(typeof value == "string" && value.length < 1){
39408             return "&#160;";
39409         }
39410         return value;
39411 };
39412
39413 // Alias for backwards compatibility
39414 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39415 /*
39416  * Based on:
39417  * Ext JS Library 1.1.1
39418  * Copyright(c) 2006-2007, Ext JS, LLC.
39419  *
39420  * Originally Released Under LGPL - original licence link has changed is not relivant.
39421  *
39422  * Fork - LGPL
39423  * <script type="text/javascript">
39424  */
39425
39426 /**
39427  * @class Roo.grid.AbstractSelectionModel
39428  * @extends Roo.util.Observable
39429  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39430  * implemented by descendant classes.  This class should not be directly instantiated.
39431  * @constructor
39432  */
39433 Roo.grid.AbstractSelectionModel = function(){
39434     this.locked = false;
39435     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39436 };
39437
39438 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39439     /** @ignore Called by the grid automatically. Do not call directly. */
39440     init : function(grid){
39441         this.grid = grid;
39442         this.initEvents();
39443     },
39444
39445     /**
39446      * Locks the selections.
39447      */
39448     lock : function(){
39449         this.locked = true;
39450     },
39451
39452     /**
39453      * Unlocks the selections.
39454      */
39455     unlock : function(){
39456         this.locked = false;
39457     },
39458
39459     /**
39460      * Returns true if the selections are locked.
39461      * @return {Boolean}
39462      */
39463     isLocked : function(){
39464         return this.locked;
39465     }
39466 });/*
39467  * Based on:
39468  * Ext JS Library 1.1.1
39469  * Copyright(c) 2006-2007, Ext JS, LLC.
39470  *
39471  * Originally Released Under LGPL - original licence link has changed is not relivant.
39472  *
39473  * Fork - LGPL
39474  * <script type="text/javascript">
39475  */
39476 /**
39477  * @extends Roo.grid.AbstractSelectionModel
39478  * @class Roo.grid.RowSelectionModel
39479  * The default SelectionModel used by {@link Roo.grid.Grid}.
39480  * It supports multiple selections and keyboard selection/navigation. 
39481  * @constructor
39482  * @param {Object} config
39483  */
39484 Roo.grid.RowSelectionModel = function(config){
39485     Roo.apply(this, config);
39486     this.selections = new Roo.util.MixedCollection(false, function(o){
39487         return o.id;
39488     });
39489
39490     this.last = false;
39491     this.lastActive = false;
39492
39493     this.addEvents({
39494         /**
39495              * @event selectionchange
39496              * Fires when the selection changes
39497              * @param {SelectionModel} this
39498              */
39499             "selectionchange" : true,
39500         /**
39501              * @event afterselectionchange
39502              * Fires after the selection changes (eg. by key press or clicking)
39503              * @param {SelectionModel} this
39504              */
39505             "afterselectionchange" : true,
39506         /**
39507              * @event beforerowselect
39508              * Fires when a row is selected being selected, return false to cancel.
39509              * @param {SelectionModel} this
39510              * @param {Number} rowIndex The selected index
39511              * @param {Boolean} keepExisting False if other selections will be cleared
39512              */
39513             "beforerowselect" : true,
39514         /**
39515              * @event rowselect
39516              * Fires when a row is selected.
39517              * @param {SelectionModel} this
39518              * @param {Number} rowIndex The selected index
39519              * @param {Roo.data.Record} r The record
39520              */
39521             "rowselect" : true,
39522         /**
39523              * @event rowdeselect
39524              * Fires when a row is deselected.
39525              * @param {SelectionModel} this
39526              * @param {Number} rowIndex The selected index
39527              */
39528         "rowdeselect" : true
39529     });
39530     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39531     this.locked = false;
39532 };
39533
39534 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39535     /**
39536      * @cfg {Boolean} singleSelect
39537      * True to allow selection of only one row at a time (defaults to false)
39538      */
39539     singleSelect : false,
39540
39541     // private
39542     initEvents : function(){
39543
39544         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39545             this.grid.on("mousedown", this.handleMouseDown, this);
39546         }else{ // allow click to work like normal
39547             this.grid.on("rowclick", this.handleDragableRowClick, this);
39548         }
39549
39550         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39551             "up" : function(e){
39552                 if(!e.shiftKey){
39553                     this.selectPrevious(e.shiftKey);
39554                 }else if(this.last !== false && this.lastActive !== false){
39555                     var last = this.last;
39556                     this.selectRange(this.last,  this.lastActive-1);
39557                     this.grid.getView().focusRow(this.lastActive);
39558                     if(last !== false){
39559                         this.last = last;
39560                     }
39561                 }else{
39562                     this.selectFirstRow();
39563                 }
39564                 this.fireEvent("afterselectionchange", this);
39565             },
39566             "down" : function(e){
39567                 if(!e.shiftKey){
39568                     this.selectNext(e.shiftKey);
39569                 }else if(this.last !== false && this.lastActive !== false){
39570                     var last = this.last;
39571                     this.selectRange(this.last,  this.lastActive+1);
39572                     this.grid.getView().focusRow(this.lastActive);
39573                     if(last !== false){
39574                         this.last = last;
39575                     }
39576                 }else{
39577                     this.selectFirstRow();
39578                 }
39579                 this.fireEvent("afterselectionchange", this);
39580             },
39581             scope: this
39582         });
39583
39584         var view = this.grid.view;
39585         view.on("refresh", this.onRefresh, this);
39586         view.on("rowupdated", this.onRowUpdated, this);
39587         view.on("rowremoved", this.onRemove, this);
39588     },
39589
39590     // private
39591     onRefresh : function(){
39592         var ds = this.grid.dataSource, i, v = this.grid.view;
39593         var s = this.selections;
39594         s.each(function(r){
39595             if((i = ds.indexOfId(r.id)) != -1){
39596                 v.onRowSelect(i);
39597                 s.add(ds.getAt(i)); // updating the selection relate data
39598             }else{
39599                 s.remove(r);
39600             }
39601         });
39602     },
39603
39604     // private
39605     onRemove : function(v, index, r){
39606         this.selections.remove(r);
39607     },
39608
39609     // private
39610     onRowUpdated : function(v, index, r){
39611         if(this.isSelected(r)){
39612             v.onRowSelect(index);
39613         }
39614     },
39615
39616     /**
39617      * Select records.
39618      * @param {Array} records The records to select
39619      * @param {Boolean} keepExisting (optional) True to keep existing selections
39620      */
39621     selectRecords : function(records, keepExisting){
39622         if(!keepExisting){
39623             this.clearSelections();
39624         }
39625         var ds = this.grid.dataSource;
39626         for(var i = 0, len = records.length; i < len; i++){
39627             this.selectRow(ds.indexOf(records[i]), true);
39628         }
39629     },
39630
39631     /**
39632      * Gets the number of selected rows.
39633      * @return {Number}
39634      */
39635     getCount : function(){
39636         return this.selections.length;
39637     },
39638
39639     /**
39640      * Selects the first row in the grid.
39641      */
39642     selectFirstRow : function(){
39643         this.selectRow(0);
39644     },
39645
39646     /**
39647      * Select the last row.
39648      * @param {Boolean} keepExisting (optional) True to keep existing selections
39649      */
39650     selectLastRow : function(keepExisting){
39651         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39652     },
39653
39654     /**
39655      * Selects the row immediately following the last selected row.
39656      * @param {Boolean} keepExisting (optional) True to keep existing selections
39657      */
39658     selectNext : function(keepExisting){
39659         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39660             this.selectRow(this.last+1, keepExisting);
39661             this.grid.getView().focusRow(this.last);
39662         }
39663     },
39664
39665     /**
39666      * Selects the row that precedes the last selected row.
39667      * @param {Boolean} keepExisting (optional) True to keep existing selections
39668      */
39669     selectPrevious : function(keepExisting){
39670         if(this.last){
39671             this.selectRow(this.last-1, keepExisting);
39672             this.grid.getView().focusRow(this.last);
39673         }
39674     },
39675
39676     /**
39677      * Returns the selected records
39678      * @return {Array} Array of selected records
39679      */
39680     getSelections : function(){
39681         return [].concat(this.selections.items);
39682     },
39683
39684     /**
39685      * Returns the first selected record.
39686      * @return {Record}
39687      */
39688     getSelected : function(){
39689         return this.selections.itemAt(0);
39690     },
39691
39692
39693     /**
39694      * Clears all selections.
39695      */
39696     clearSelections : function(fast){
39697         if(this.locked) {
39698             return;
39699         }
39700         if(fast !== true){
39701             var ds = this.grid.dataSource;
39702             var s = this.selections;
39703             s.each(function(r){
39704                 this.deselectRow(ds.indexOfId(r.id));
39705             }, this);
39706             s.clear();
39707         }else{
39708             this.selections.clear();
39709         }
39710         this.last = false;
39711     },
39712
39713
39714     /**
39715      * Selects all rows.
39716      */
39717     selectAll : function(){
39718         if(this.locked) {
39719             return;
39720         }
39721         this.selections.clear();
39722         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39723             this.selectRow(i, true);
39724         }
39725     },
39726
39727     /**
39728      * Returns True if there is a selection.
39729      * @return {Boolean}
39730      */
39731     hasSelection : function(){
39732         return this.selections.length > 0;
39733     },
39734
39735     /**
39736      * Returns True if the specified row is selected.
39737      * @param {Number/Record} record The record or index of the record to check
39738      * @return {Boolean}
39739      */
39740     isSelected : function(index){
39741         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39742         return (r && this.selections.key(r.id) ? true : false);
39743     },
39744
39745     /**
39746      * Returns True if the specified record id is selected.
39747      * @param {String} id The id of record to check
39748      * @return {Boolean}
39749      */
39750     isIdSelected : function(id){
39751         return (this.selections.key(id) ? true : false);
39752     },
39753
39754     // private
39755     handleMouseDown : function(e, t){
39756         var view = this.grid.getView(), rowIndex;
39757         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39758             return;
39759         };
39760         if(e.shiftKey && this.last !== false){
39761             var last = this.last;
39762             this.selectRange(last, rowIndex, e.ctrlKey);
39763             this.last = last; // reset the last
39764             view.focusRow(rowIndex);
39765         }else{
39766             var isSelected = this.isSelected(rowIndex);
39767             if(e.button !== 0 && isSelected){
39768                 view.focusRow(rowIndex);
39769             }else if(e.ctrlKey && isSelected){
39770                 this.deselectRow(rowIndex);
39771             }else if(!isSelected){
39772                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39773                 view.focusRow(rowIndex);
39774             }
39775         }
39776         this.fireEvent("afterselectionchange", this);
39777     },
39778     // private
39779     handleDragableRowClick :  function(grid, rowIndex, e) 
39780     {
39781         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39782             this.selectRow(rowIndex, false);
39783             grid.view.focusRow(rowIndex);
39784              this.fireEvent("afterselectionchange", this);
39785         }
39786     },
39787     
39788     /**
39789      * Selects multiple rows.
39790      * @param {Array} rows Array of the indexes of the row to select
39791      * @param {Boolean} keepExisting (optional) True to keep existing selections
39792      */
39793     selectRows : function(rows, keepExisting){
39794         if(!keepExisting){
39795             this.clearSelections();
39796         }
39797         for(var i = 0, len = rows.length; i < len; i++){
39798             this.selectRow(rows[i], true);
39799         }
39800     },
39801
39802     /**
39803      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39804      * @param {Number} startRow The index of the first row in the range
39805      * @param {Number} endRow The index of the last row in the range
39806      * @param {Boolean} keepExisting (optional) True to retain existing selections
39807      */
39808     selectRange : function(startRow, endRow, keepExisting){
39809         if(this.locked) {
39810             return;
39811         }
39812         if(!keepExisting){
39813             this.clearSelections();
39814         }
39815         if(startRow <= endRow){
39816             for(var i = startRow; i <= endRow; i++){
39817                 this.selectRow(i, true);
39818             }
39819         }else{
39820             for(var i = startRow; i >= endRow; i--){
39821                 this.selectRow(i, true);
39822             }
39823         }
39824     },
39825
39826     /**
39827      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39828      * @param {Number} startRow The index of the first row in the range
39829      * @param {Number} endRow The index of the last row in the range
39830      */
39831     deselectRange : function(startRow, endRow, preventViewNotify){
39832         if(this.locked) {
39833             return;
39834         }
39835         for(var i = startRow; i <= endRow; i++){
39836             this.deselectRow(i, preventViewNotify);
39837         }
39838     },
39839
39840     /**
39841      * Selects a row.
39842      * @param {Number} row The index of the row to select
39843      * @param {Boolean} keepExisting (optional) True to keep existing selections
39844      */
39845     selectRow : function(index, keepExisting, preventViewNotify){
39846         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39847             return;
39848         }
39849         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39850             if(!keepExisting || this.singleSelect){
39851                 this.clearSelections();
39852             }
39853             var r = this.grid.dataSource.getAt(index);
39854             this.selections.add(r);
39855             this.last = this.lastActive = index;
39856             if(!preventViewNotify){
39857                 this.grid.getView().onRowSelect(index);
39858             }
39859             this.fireEvent("rowselect", this, index, r);
39860             this.fireEvent("selectionchange", this);
39861         }
39862     },
39863
39864     /**
39865      * Deselects a row.
39866      * @param {Number} row The index of the row to deselect
39867      */
39868     deselectRow : function(index, preventViewNotify){
39869         if(this.locked) {
39870             return;
39871         }
39872         if(this.last == index){
39873             this.last = false;
39874         }
39875         if(this.lastActive == index){
39876             this.lastActive = false;
39877         }
39878         var r = this.grid.dataSource.getAt(index);
39879         this.selections.remove(r);
39880         if(!preventViewNotify){
39881             this.grid.getView().onRowDeselect(index);
39882         }
39883         this.fireEvent("rowdeselect", this, index);
39884         this.fireEvent("selectionchange", this);
39885     },
39886
39887     // private
39888     restoreLast : function(){
39889         if(this._last){
39890             this.last = this._last;
39891         }
39892     },
39893
39894     // private
39895     acceptsNav : function(row, col, cm){
39896         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39897     },
39898
39899     // private
39900     onEditorKey : function(field, e){
39901         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39902         if(k == e.TAB){
39903             e.stopEvent();
39904             ed.completeEdit();
39905             if(e.shiftKey){
39906                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39907             }else{
39908                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39909             }
39910         }else if(k == e.ENTER && !e.ctrlKey){
39911             e.stopEvent();
39912             ed.completeEdit();
39913             if(e.shiftKey){
39914                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39915             }else{
39916                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39917             }
39918         }else if(k == e.ESC){
39919             ed.cancelEdit();
39920         }
39921         if(newCell){
39922             g.startEditing(newCell[0], newCell[1]);
39923         }
39924     }
39925 });/*
39926  * Based on:
39927  * Ext JS Library 1.1.1
39928  * Copyright(c) 2006-2007, Ext JS, LLC.
39929  *
39930  * Originally Released Under LGPL - original licence link has changed is not relivant.
39931  *
39932  * Fork - LGPL
39933  * <script type="text/javascript">
39934  */
39935 /**
39936  * @class Roo.grid.CellSelectionModel
39937  * @extends Roo.grid.AbstractSelectionModel
39938  * This class provides the basic implementation for cell selection in a grid.
39939  * @constructor
39940  * @param {Object} config The object containing the configuration of this model.
39941  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39942  */
39943 Roo.grid.CellSelectionModel = function(config){
39944     Roo.apply(this, config);
39945
39946     this.selection = null;
39947
39948     this.addEvents({
39949         /**
39950              * @event beforerowselect
39951              * Fires before a cell is selected.
39952              * @param {SelectionModel} this
39953              * @param {Number} rowIndex The selected row index
39954              * @param {Number} colIndex The selected cell index
39955              */
39956             "beforecellselect" : true,
39957         /**
39958              * @event cellselect
39959              * Fires when a cell is selected.
39960              * @param {SelectionModel} this
39961              * @param {Number} rowIndex The selected row index
39962              * @param {Number} colIndex The selected cell index
39963              */
39964             "cellselect" : true,
39965         /**
39966              * @event selectionchange
39967              * Fires when the active selection changes.
39968              * @param {SelectionModel} this
39969              * @param {Object} selection null for no selection or an object (o) with two properties
39970                 <ul>
39971                 <li>o.record: the record object for the row the selection is in</li>
39972                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39973                 </ul>
39974              */
39975             "selectionchange" : true,
39976         /**
39977              * @event tabend
39978              * Fires when the tab (or enter) was pressed on the last editable cell
39979              * You can use this to trigger add new row.
39980              * @param {SelectionModel} this
39981              */
39982             "tabend" : true,
39983          /**
39984              * @event beforeeditnext
39985              * Fires before the next editable sell is made active
39986              * You can use this to skip to another cell or fire the tabend
39987              *    if you set cell to false
39988              * @param {Object} eventdata object : { cell : [ row, col ] } 
39989              */
39990             "beforeeditnext" : true
39991     });
39992     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39993 };
39994
39995 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39996     
39997     enter_is_tab: false,
39998
39999     /** @ignore */
40000     initEvents : function(){
40001         this.grid.on("mousedown", this.handleMouseDown, this);
40002         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40003         var view = this.grid.view;
40004         view.on("refresh", this.onViewChange, this);
40005         view.on("rowupdated", this.onRowUpdated, this);
40006         view.on("beforerowremoved", this.clearSelections, this);
40007         view.on("beforerowsinserted", this.clearSelections, this);
40008         if(this.grid.isEditor){
40009             this.grid.on("beforeedit", this.beforeEdit,  this);
40010         }
40011     },
40012
40013         //private
40014     beforeEdit : function(e){
40015         this.select(e.row, e.column, false, true, e.record);
40016     },
40017
40018         //private
40019     onRowUpdated : function(v, index, r){
40020         if(this.selection && this.selection.record == r){
40021             v.onCellSelect(index, this.selection.cell[1]);
40022         }
40023     },
40024
40025         //private
40026     onViewChange : function(){
40027         this.clearSelections(true);
40028     },
40029
40030         /**
40031          * Returns the currently selected cell,.
40032          * @return {Array} The selected cell (row, column) or null if none selected.
40033          */
40034     getSelectedCell : function(){
40035         return this.selection ? this.selection.cell : null;
40036     },
40037
40038     /**
40039      * Clears all selections.
40040      * @param {Boolean} true to prevent the gridview from being notified about the change.
40041      */
40042     clearSelections : function(preventNotify){
40043         var s = this.selection;
40044         if(s){
40045             if(preventNotify !== true){
40046                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40047             }
40048             this.selection = null;
40049             this.fireEvent("selectionchange", this, null);
40050         }
40051     },
40052
40053     /**
40054      * Returns true if there is a selection.
40055      * @return {Boolean}
40056      */
40057     hasSelection : function(){
40058         return this.selection ? true : false;
40059     },
40060
40061     /** @ignore */
40062     handleMouseDown : function(e, t){
40063         var v = this.grid.getView();
40064         if(this.isLocked()){
40065             return;
40066         };
40067         var row = v.findRowIndex(t);
40068         var cell = v.findCellIndex(t);
40069         if(row !== false && cell !== false){
40070             this.select(row, cell);
40071         }
40072     },
40073
40074     /**
40075      * Selects a cell.
40076      * @param {Number} rowIndex
40077      * @param {Number} collIndex
40078      */
40079     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40080         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40081             this.clearSelections();
40082             r = r || this.grid.dataSource.getAt(rowIndex);
40083             this.selection = {
40084                 record : r,
40085                 cell : [rowIndex, colIndex]
40086             };
40087             if(!preventViewNotify){
40088                 var v = this.grid.getView();
40089                 v.onCellSelect(rowIndex, colIndex);
40090                 if(preventFocus !== true){
40091                     v.focusCell(rowIndex, colIndex);
40092                 }
40093             }
40094             this.fireEvent("cellselect", this, rowIndex, colIndex);
40095             this.fireEvent("selectionchange", this, this.selection);
40096         }
40097     },
40098
40099         //private
40100     isSelectable : function(rowIndex, colIndex, cm){
40101         return !cm.isHidden(colIndex);
40102     },
40103
40104     /** @ignore */
40105     handleKeyDown : function(e){
40106         //Roo.log('Cell Sel Model handleKeyDown');
40107         if(!e.isNavKeyPress()){
40108             return;
40109         }
40110         var g = this.grid, s = this.selection;
40111         if(!s){
40112             e.stopEvent();
40113             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40114             if(cell){
40115                 this.select(cell[0], cell[1]);
40116             }
40117             return;
40118         }
40119         var sm = this;
40120         var walk = function(row, col, step){
40121             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40122         };
40123         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40124         var newCell;
40125
40126       
40127
40128         switch(k){
40129             case e.TAB:
40130                 // handled by onEditorKey
40131                 if (g.isEditor && g.editing) {
40132                     return;
40133                 }
40134                 if(e.shiftKey) {
40135                     newCell = walk(r, c-1, -1);
40136                 } else {
40137                     newCell = walk(r, c+1, 1);
40138                 }
40139                 break;
40140             
40141             case e.DOWN:
40142                newCell = walk(r+1, c, 1);
40143                 break;
40144             
40145             case e.UP:
40146                 newCell = walk(r-1, c, -1);
40147                 break;
40148             
40149             case e.RIGHT:
40150                 newCell = walk(r, c+1, 1);
40151                 break;
40152             
40153             case e.LEFT:
40154                 newCell = walk(r, c-1, -1);
40155                 break;
40156             
40157             case e.ENTER:
40158                 
40159                 if(g.isEditor && !g.editing){
40160                    g.startEditing(r, c);
40161                    e.stopEvent();
40162                    return;
40163                 }
40164                 
40165                 
40166              break;
40167         };
40168         if(newCell){
40169             this.select(newCell[0], newCell[1]);
40170             e.stopEvent();
40171             
40172         }
40173     },
40174
40175     acceptsNav : function(row, col, cm){
40176         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40177     },
40178     /**
40179      * Selects a cell.
40180      * @param {Number} field (not used) - as it's normally used as a listener
40181      * @param {Number} e - event - fake it by using
40182      *
40183      * var e = Roo.EventObjectImpl.prototype;
40184      * e.keyCode = e.TAB
40185      *
40186      * 
40187      */
40188     onEditorKey : function(field, e){
40189         
40190         var k = e.getKey(),
40191             newCell,
40192             g = this.grid,
40193             ed = g.activeEditor,
40194             forward = false;
40195         ///Roo.log('onEditorKey' + k);
40196         
40197         
40198         if (this.enter_is_tab && k == e.ENTER) {
40199             k = e.TAB;
40200         }
40201         
40202         if(k == e.TAB){
40203             if(e.shiftKey){
40204                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40205             }else{
40206                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40207                 forward = true;
40208             }
40209             
40210             e.stopEvent();
40211             
40212         } else if(k == e.ENTER &&  !e.ctrlKey){
40213             ed.completeEdit();
40214             e.stopEvent();
40215             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40216         
40217                 } else if(k == e.ESC){
40218             ed.cancelEdit();
40219         }
40220                 
40221         if (newCell) {
40222             var ecall = { cell : newCell, forward : forward };
40223             this.fireEvent('beforeeditnext', ecall );
40224             newCell = ecall.cell;
40225                         forward = ecall.forward;
40226         }
40227                 
40228         if(newCell){
40229             //Roo.log('next cell after edit');
40230             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40231         } else if (forward) {
40232             // tabbed past last
40233             this.fireEvent.defer(100, this, ['tabend',this]);
40234         }
40235     }
40236 });/*
40237  * Based on:
40238  * Ext JS Library 1.1.1
40239  * Copyright(c) 2006-2007, Ext JS, LLC.
40240  *
40241  * Originally Released Under LGPL - original licence link has changed is not relivant.
40242  *
40243  * Fork - LGPL
40244  * <script type="text/javascript">
40245  */
40246  
40247 /**
40248  * @class Roo.grid.EditorGrid
40249  * @extends Roo.grid.Grid
40250  * Class for creating and editable grid.
40251  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40252  * The container MUST have some type of size defined for the grid to fill. The container will be 
40253  * automatically set to position relative if it isn't already.
40254  * @param {Object} dataSource The data model to bind to
40255  * @param {Object} colModel The column model with info about this grid's columns
40256  */
40257 Roo.grid.EditorGrid = function(container, config){
40258     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40259     this.getGridEl().addClass("xedit-grid");
40260
40261     if(!this.selModel){
40262         this.selModel = new Roo.grid.CellSelectionModel();
40263     }
40264
40265     this.activeEditor = null;
40266
40267         this.addEvents({
40268             /**
40269              * @event beforeedit
40270              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40271              * <ul style="padding:5px;padding-left:16px;">
40272              * <li>grid - This grid</li>
40273              * <li>record - The record being edited</li>
40274              * <li>field - The field name being edited</li>
40275              * <li>value - The value for the field being edited.</li>
40276              * <li>row - The grid row index</li>
40277              * <li>column - The grid column index</li>
40278              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40279              * </ul>
40280              * @param {Object} e An edit event (see above for description)
40281              */
40282             "beforeedit" : true,
40283             /**
40284              * @event afteredit
40285              * Fires after a cell is edited. <br />
40286              * <ul style="padding:5px;padding-left:16px;">
40287              * <li>grid - This grid</li>
40288              * <li>record - The record being edited</li>
40289              * <li>field - The field name being edited</li>
40290              * <li>value - The value being set</li>
40291              * <li>originalValue - The original value for the field, before the edit.</li>
40292              * <li>row - The grid row index</li>
40293              * <li>column - The grid column index</li>
40294              * </ul>
40295              * @param {Object} e An edit event (see above for description)
40296              */
40297             "afteredit" : true,
40298             /**
40299              * @event validateedit
40300              * Fires after a cell is edited, but before the value is set in the record. 
40301          * You can use this to modify the value being set in the field, Return false
40302              * to cancel the change. The edit event object has the following properties <br />
40303              * <ul style="padding:5px;padding-left:16px;">
40304          * <li>editor - This editor</li>
40305              * <li>grid - This grid</li>
40306              * <li>record - The record being edited</li>
40307              * <li>field - The field name being edited</li>
40308              * <li>value - The value being set</li>
40309              * <li>originalValue - The original value for the field, before the edit.</li>
40310              * <li>row - The grid row index</li>
40311              * <li>column - The grid column index</li>
40312              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40313              * </ul>
40314              * @param {Object} e An edit event (see above for description)
40315              */
40316             "validateedit" : true
40317         });
40318     this.on("bodyscroll", this.stopEditing,  this);
40319     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40320 };
40321
40322 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40323     /**
40324      * @cfg {Number} clicksToEdit
40325      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40326      */
40327     clicksToEdit: 2,
40328
40329     // private
40330     isEditor : true,
40331     // private
40332     trackMouseOver: false, // causes very odd FF errors
40333
40334     onCellDblClick : function(g, row, col){
40335         this.startEditing(row, col);
40336     },
40337
40338     onEditComplete : function(ed, value, startValue){
40339         this.editing = false;
40340         this.activeEditor = null;
40341         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40342         var r = ed.record;
40343         var field = this.colModel.getDataIndex(ed.col);
40344         var e = {
40345             grid: this,
40346             record: r,
40347             field: field,
40348             originalValue: startValue,
40349             value: value,
40350             row: ed.row,
40351             column: ed.col,
40352             cancel:false,
40353             editor: ed
40354         };
40355         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40356         cell.show();
40357           
40358         if(String(value) !== String(startValue)){
40359             
40360             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40361                 r.set(field, e.value);
40362                 // if we are dealing with a combo box..
40363                 // then we also set the 'name' colum to be the displayField
40364                 if (ed.field.displayField && ed.field.name) {
40365                     r.set(ed.field.name, ed.field.el.dom.value);
40366                 }
40367                 
40368                 delete e.cancel; //?? why!!!
40369                 this.fireEvent("afteredit", e);
40370             }
40371         } else {
40372             this.fireEvent("afteredit", e); // always fire it!
40373         }
40374         this.view.focusCell(ed.row, ed.col);
40375     },
40376
40377     /**
40378      * Starts editing the specified for the specified row/column
40379      * @param {Number} rowIndex
40380      * @param {Number} colIndex
40381      */
40382     startEditing : function(row, col){
40383         this.stopEditing();
40384         if(this.colModel.isCellEditable(col, row)){
40385             this.view.ensureVisible(row, col, true);
40386           
40387             var r = this.dataSource.getAt(row);
40388             var field = this.colModel.getDataIndex(col);
40389             var cell = Roo.get(this.view.getCell(row,col));
40390             var e = {
40391                 grid: this,
40392                 record: r,
40393                 field: field,
40394                 value: r.data[field],
40395                 row: row,
40396                 column: col,
40397                 cancel:false 
40398             };
40399             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40400                 this.editing = true;
40401                 var ed = this.colModel.getCellEditor(col, row);
40402                 
40403                 if (!ed) {
40404                     return;
40405                 }
40406                 if(!ed.rendered){
40407                     ed.render(ed.parentEl || document.body);
40408                 }
40409                 ed.field.reset();
40410                
40411                 cell.hide();
40412                 
40413                 (function(){ // complex but required for focus issues in safari, ie and opera
40414                     ed.row = row;
40415                     ed.col = col;
40416                     ed.record = r;
40417                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40418                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40419                     this.activeEditor = ed;
40420                     var v = r.data[field];
40421                     ed.startEdit(this.view.getCell(row, col), v);
40422                     // combo's with 'displayField and name set
40423                     if (ed.field.displayField && ed.field.name) {
40424                         ed.field.el.dom.value = r.data[ed.field.name];
40425                     }
40426                     
40427                     
40428                 }).defer(50, this);
40429             }
40430         }
40431     },
40432         
40433     /**
40434      * Stops any active editing
40435      */
40436     stopEditing : function(){
40437         if(this.activeEditor){
40438             this.activeEditor.completeEdit();
40439         }
40440         this.activeEditor = null;
40441     },
40442         
40443          /**
40444      * Called to get grid's drag proxy text, by default returns this.ddText.
40445      * @return {String}
40446      */
40447     getDragDropText : function(){
40448         var count = this.selModel.getSelectedCell() ? 1 : 0;
40449         return String.format(this.ddText, count, count == 1 ? '' : 's');
40450     }
40451         
40452 });/*
40453  * Based on:
40454  * Ext JS Library 1.1.1
40455  * Copyright(c) 2006-2007, Ext JS, LLC.
40456  *
40457  * Originally Released Under LGPL - original licence link has changed is not relivant.
40458  *
40459  * Fork - LGPL
40460  * <script type="text/javascript">
40461  */
40462
40463 // private - not really -- you end up using it !
40464 // This is a support class used internally by the Grid components
40465
40466 /**
40467  * @class Roo.grid.GridEditor
40468  * @extends Roo.Editor
40469  * Class for creating and editable grid elements.
40470  * @param {Object} config any settings (must include field)
40471  */
40472 Roo.grid.GridEditor = function(field, config){
40473     if (!config && field.field) {
40474         config = field;
40475         field = Roo.factory(config.field, Roo.form);
40476     }
40477     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40478     field.monitorTab = false;
40479 };
40480
40481 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40482     
40483     /**
40484      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40485      */
40486     
40487     alignment: "tl-tl",
40488     autoSize: "width",
40489     hideEl : false,
40490     cls: "x-small-editor x-grid-editor",
40491     shim:false,
40492     shadow:"frame"
40493 });/*
40494  * Based on:
40495  * Ext JS Library 1.1.1
40496  * Copyright(c) 2006-2007, Ext JS, LLC.
40497  *
40498  * Originally Released Under LGPL - original licence link has changed is not relivant.
40499  *
40500  * Fork - LGPL
40501  * <script type="text/javascript">
40502  */
40503   
40504
40505   
40506 Roo.grid.PropertyRecord = Roo.data.Record.create([
40507     {name:'name',type:'string'},  'value'
40508 ]);
40509
40510
40511 Roo.grid.PropertyStore = function(grid, source){
40512     this.grid = grid;
40513     this.store = new Roo.data.Store({
40514         recordType : Roo.grid.PropertyRecord
40515     });
40516     this.store.on('update', this.onUpdate,  this);
40517     if(source){
40518         this.setSource(source);
40519     }
40520     Roo.grid.PropertyStore.superclass.constructor.call(this);
40521 };
40522
40523
40524
40525 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40526     setSource : function(o){
40527         this.source = o;
40528         this.store.removeAll();
40529         var data = [];
40530         for(var k in o){
40531             if(this.isEditableValue(o[k])){
40532                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40533             }
40534         }
40535         this.store.loadRecords({records: data}, {}, true);
40536     },
40537
40538     onUpdate : function(ds, record, type){
40539         if(type == Roo.data.Record.EDIT){
40540             var v = record.data['value'];
40541             var oldValue = record.modified['value'];
40542             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40543                 this.source[record.id] = v;
40544                 record.commit();
40545                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40546             }else{
40547                 record.reject();
40548             }
40549         }
40550     },
40551
40552     getProperty : function(row){
40553        return this.store.getAt(row);
40554     },
40555
40556     isEditableValue: function(val){
40557         if(val && val instanceof Date){
40558             return true;
40559         }else if(typeof val == 'object' || typeof val == 'function'){
40560             return false;
40561         }
40562         return true;
40563     },
40564
40565     setValue : function(prop, value){
40566         this.source[prop] = value;
40567         this.store.getById(prop).set('value', value);
40568     },
40569
40570     getSource : function(){
40571         return this.source;
40572     }
40573 });
40574
40575 Roo.grid.PropertyColumnModel = function(grid, store){
40576     this.grid = grid;
40577     var g = Roo.grid;
40578     g.PropertyColumnModel.superclass.constructor.call(this, [
40579         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40580         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40581     ]);
40582     this.store = store;
40583     this.bselect = Roo.DomHelper.append(document.body, {
40584         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40585             {tag: 'option', value: 'true', html: 'true'},
40586             {tag: 'option', value: 'false', html: 'false'}
40587         ]
40588     });
40589     Roo.id(this.bselect);
40590     var f = Roo.form;
40591     this.editors = {
40592         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40593         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40594         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40595         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40596         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40597     };
40598     this.renderCellDelegate = this.renderCell.createDelegate(this);
40599     this.renderPropDelegate = this.renderProp.createDelegate(this);
40600 };
40601
40602 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40603     
40604     
40605     nameText : 'Name',
40606     valueText : 'Value',
40607     
40608     dateFormat : 'm/j/Y',
40609     
40610     
40611     renderDate : function(dateVal){
40612         return dateVal.dateFormat(this.dateFormat);
40613     },
40614
40615     renderBool : function(bVal){
40616         return bVal ? 'true' : 'false';
40617     },
40618
40619     isCellEditable : function(colIndex, rowIndex){
40620         return colIndex == 1;
40621     },
40622
40623     getRenderer : function(col){
40624         return col == 1 ?
40625             this.renderCellDelegate : this.renderPropDelegate;
40626     },
40627
40628     renderProp : function(v){
40629         return this.getPropertyName(v);
40630     },
40631
40632     renderCell : function(val){
40633         var rv = val;
40634         if(val instanceof Date){
40635             rv = this.renderDate(val);
40636         }else if(typeof val == 'boolean'){
40637             rv = this.renderBool(val);
40638         }
40639         return Roo.util.Format.htmlEncode(rv);
40640     },
40641
40642     getPropertyName : function(name){
40643         var pn = this.grid.propertyNames;
40644         return pn && pn[name] ? pn[name] : name;
40645     },
40646
40647     getCellEditor : function(colIndex, rowIndex){
40648         var p = this.store.getProperty(rowIndex);
40649         var n = p.data['name'], val = p.data['value'];
40650         
40651         if(typeof(this.grid.customEditors[n]) == 'string'){
40652             return this.editors[this.grid.customEditors[n]];
40653         }
40654         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40655             return this.grid.customEditors[n];
40656         }
40657         if(val instanceof Date){
40658             return this.editors['date'];
40659         }else if(typeof val == 'number'){
40660             return this.editors['number'];
40661         }else if(typeof val == 'boolean'){
40662             return this.editors['boolean'];
40663         }else{
40664             return this.editors['string'];
40665         }
40666     }
40667 });
40668
40669 /**
40670  * @class Roo.grid.PropertyGrid
40671  * @extends Roo.grid.EditorGrid
40672  * This class represents the  interface of a component based property grid control.
40673  * <br><br>Usage:<pre><code>
40674  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40675       
40676  });
40677  // set any options
40678  grid.render();
40679  * </code></pre>
40680   
40681  * @constructor
40682  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40683  * The container MUST have some type of size defined for the grid to fill. The container will be
40684  * automatically set to position relative if it isn't already.
40685  * @param {Object} config A config object that sets properties on this grid.
40686  */
40687 Roo.grid.PropertyGrid = function(container, config){
40688     config = config || {};
40689     var store = new Roo.grid.PropertyStore(this);
40690     this.store = store;
40691     var cm = new Roo.grid.PropertyColumnModel(this, store);
40692     store.store.sort('name', 'ASC');
40693     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40694         ds: store.store,
40695         cm: cm,
40696         enableColLock:false,
40697         enableColumnMove:false,
40698         stripeRows:false,
40699         trackMouseOver: false,
40700         clicksToEdit:1
40701     }, config));
40702     this.getGridEl().addClass('x-props-grid');
40703     this.lastEditRow = null;
40704     this.on('columnresize', this.onColumnResize, this);
40705     this.addEvents({
40706          /**
40707              * @event beforepropertychange
40708              * Fires before a property changes (return false to stop?)
40709              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40710              * @param {String} id Record Id
40711              * @param {String} newval New Value
40712          * @param {String} oldval Old Value
40713              */
40714         "beforepropertychange": true,
40715         /**
40716              * @event propertychange
40717              * Fires after a property changes
40718              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40719              * @param {String} id Record Id
40720              * @param {String} newval New Value
40721          * @param {String} oldval Old Value
40722              */
40723         "propertychange": true
40724     });
40725     this.customEditors = this.customEditors || {};
40726 };
40727 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40728     
40729      /**
40730      * @cfg {Object} customEditors map of colnames=> custom editors.
40731      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40732      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40733      * false disables editing of the field.
40734          */
40735     
40736       /**
40737      * @cfg {Object} propertyNames map of property Names to their displayed value
40738          */
40739     
40740     render : function(){
40741         Roo.grid.PropertyGrid.superclass.render.call(this);
40742         this.autoSize.defer(100, this);
40743     },
40744
40745     autoSize : function(){
40746         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40747         if(this.view){
40748             this.view.fitColumns();
40749         }
40750     },
40751
40752     onColumnResize : function(){
40753         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40754         this.autoSize();
40755     },
40756     /**
40757      * Sets the data for the Grid
40758      * accepts a Key => Value object of all the elements avaiable.
40759      * @param {Object} data  to appear in grid.
40760      */
40761     setSource : function(source){
40762         this.store.setSource(source);
40763         //this.autoSize();
40764     },
40765     /**
40766      * Gets all the data from the grid.
40767      * @return {Object} data  data stored in grid
40768      */
40769     getSource : function(){
40770         return this.store.getSource();
40771     }
40772 });/*
40773   
40774  * Licence LGPL
40775  
40776  */
40777  
40778 /**
40779  * @class Roo.grid.Calendar
40780  * @extends Roo.util.Grid
40781  * This class extends the Grid to provide a calendar widget
40782  * <br><br>Usage:<pre><code>
40783  var grid = new Roo.grid.Calendar("my-container-id", {
40784      ds: myDataStore,
40785      cm: myColModel,
40786      selModel: mySelectionModel,
40787      autoSizeColumns: true,
40788      monitorWindowResize: false,
40789      trackMouseOver: true
40790      eventstore : real data store..
40791  });
40792  // set any options
40793  grid.render();
40794   
40795   * @constructor
40796  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40797  * The container MUST have some type of size defined for the grid to fill. The container will be
40798  * automatically set to position relative if it isn't already.
40799  * @param {Object} config A config object that sets properties on this grid.
40800  */
40801 Roo.grid.Calendar = function(container, config){
40802         // initialize the container
40803         this.container = Roo.get(container);
40804         this.container.update("");
40805         this.container.setStyle("overflow", "hidden");
40806     this.container.addClass('x-grid-container');
40807
40808     this.id = this.container.id;
40809
40810     Roo.apply(this, config);
40811     // check and correct shorthanded configs
40812     
40813     var rows = [];
40814     var d =1;
40815     for (var r = 0;r < 6;r++) {
40816         
40817         rows[r]=[];
40818         for (var c =0;c < 7;c++) {
40819             rows[r][c]= '';
40820         }
40821     }
40822     if (this.eventStore) {
40823         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40824         this.eventStore.on('load',this.onLoad, this);
40825         this.eventStore.on('beforeload',this.clearEvents, this);
40826          
40827     }
40828     
40829     this.dataSource = new Roo.data.Store({
40830             proxy: new Roo.data.MemoryProxy(rows),
40831             reader: new Roo.data.ArrayReader({}, [
40832                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40833     });
40834
40835     this.dataSource.load();
40836     this.ds = this.dataSource;
40837     this.ds.xmodule = this.xmodule || false;
40838     
40839     
40840     var cellRender = function(v,x,r)
40841     {
40842         return String.format(
40843             '<div class="fc-day  fc-widget-content"><div>' +
40844                 '<div class="fc-event-container"></div>' +
40845                 '<div class="fc-day-number">{0}</div>'+
40846                 
40847                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40848             '</div></div>', v);
40849     
40850     }
40851     
40852     
40853     this.colModel = new Roo.grid.ColumnModel( [
40854         {
40855             xtype: 'ColumnModel',
40856             xns: Roo.grid,
40857             dataIndex : 'weekday0',
40858             header : 'Sunday',
40859             renderer : cellRender
40860         },
40861         {
40862             xtype: 'ColumnModel',
40863             xns: Roo.grid,
40864             dataIndex : 'weekday1',
40865             header : 'Monday',
40866             renderer : cellRender
40867         },
40868         {
40869             xtype: 'ColumnModel',
40870             xns: Roo.grid,
40871             dataIndex : 'weekday2',
40872             header : 'Tuesday',
40873             renderer : cellRender
40874         },
40875         {
40876             xtype: 'ColumnModel',
40877             xns: Roo.grid,
40878             dataIndex : 'weekday3',
40879             header : 'Wednesday',
40880             renderer : cellRender
40881         },
40882         {
40883             xtype: 'ColumnModel',
40884             xns: Roo.grid,
40885             dataIndex : 'weekday4',
40886             header : 'Thursday',
40887             renderer : cellRender
40888         },
40889         {
40890             xtype: 'ColumnModel',
40891             xns: Roo.grid,
40892             dataIndex : 'weekday5',
40893             header : 'Friday',
40894             renderer : cellRender
40895         },
40896         {
40897             xtype: 'ColumnModel',
40898             xns: Roo.grid,
40899             dataIndex : 'weekday6',
40900             header : 'Saturday',
40901             renderer : cellRender
40902         }
40903     ]);
40904     this.cm = this.colModel;
40905     this.cm.xmodule = this.xmodule || false;
40906  
40907         
40908           
40909     //this.selModel = new Roo.grid.CellSelectionModel();
40910     //this.sm = this.selModel;
40911     //this.selModel.init(this);
40912     
40913     
40914     if(this.width){
40915         this.container.setWidth(this.width);
40916     }
40917
40918     if(this.height){
40919         this.container.setHeight(this.height);
40920     }
40921     /** @private */
40922         this.addEvents({
40923         // raw events
40924         /**
40925          * @event click
40926          * The raw click event for the entire grid.
40927          * @param {Roo.EventObject} e
40928          */
40929         "click" : true,
40930         /**
40931          * @event dblclick
40932          * The raw dblclick event for the entire grid.
40933          * @param {Roo.EventObject} e
40934          */
40935         "dblclick" : true,
40936         /**
40937          * @event contextmenu
40938          * The raw contextmenu event for the entire grid.
40939          * @param {Roo.EventObject} e
40940          */
40941         "contextmenu" : true,
40942         /**
40943          * @event mousedown
40944          * The raw mousedown event for the entire grid.
40945          * @param {Roo.EventObject} e
40946          */
40947         "mousedown" : true,
40948         /**
40949          * @event mouseup
40950          * The raw mouseup event for the entire grid.
40951          * @param {Roo.EventObject} e
40952          */
40953         "mouseup" : true,
40954         /**
40955          * @event mouseover
40956          * The raw mouseover event for the entire grid.
40957          * @param {Roo.EventObject} e
40958          */
40959         "mouseover" : true,
40960         /**
40961          * @event mouseout
40962          * The raw mouseout event for the entire grid.
40963          * @param {Roo.EventObject} e
40964          */
40965         "mouseout" : true,
40966         /**
40967          * @event keypress
40968          * The raw keypress event for the entire grid.
40969          * @param {Roo.EventObject} e
40970          */
40971         "keypress" : true,
40972         /**
40973          * @event keydown
40974          * The raw keydown event for the entire grid.
40975          * @param {Roo.EventObject} e
40976          */
40977         "keydown" : true,
40978
40979         // custom events
40980
40981         /**
40982          * @event cellclick
40983          * Fires when a cell is clicked
40984          * @param {Grid} this
40985          * @param {Number} rowIndex
40986          * @param {Number} columnIndex
40987          * @param {Roo.EventObject} e
40988          */
40989         "cellclick" : true,
40990         /**
40991          * @event celldblclick
40992          * Fires when a cell is double clicked
40993          * @param {Grid} this
40994          * @param {Number} rowIndex
40995          * @param {Number} columnIndex
40996          * @param {Roo.EventObject} e
40997          */
40998         "celldblclick" : true,
40999         /**
41000          * @event rowclick
41001          * Fires when a row is clicked
41002          * @param {Grid} this
41003          * @param {Number} rowIndex
41004          * @param {Roo.EventObject} e
41005          */
41006         "rowclick" : true,
41007         /**
41008          * @event rowdblclick
41009          * Fires when a row is double clicked
41010          * @param {Grid} this
41011          * @param {Number} rowIndex
41012          * @param {Roo.EventObject} e
41013          */
41014         "rowdblclick" : true,
41015         /**
41016          * @event headerclick
41017          * Fires when a header is clicked
41018          * @param {Grid} this
41019          * @param {Number} columnIndex
41020          * @param {Roo.EventObject} e
41021          */
41022         "headerclick" : true,
41023         /**
41024          * @event headerdblclick
41025          * Fires when a header cell is double clicked
41026          * @param {Grid} this
41027          * @param {Number} columnIndex
41028          * @param {Roo.EventObject} e
41029          */
41030         "headerdblclick" : true,
41031         /**
41032          * @event rowcontextmenu
41033          * Fires when a row is right clicked
41034          * @param {Grid} this
41035          * @param {Number} rowIndex
41036          * @param {Roo.EventObject} e
41037          */
41038         "rowcontextmenu" : true,
41039         /**
41040          * @event cellcontextmenu
41041          * Fires when a cell is right clicked
41042          * @param {Grid} this
41043          * @param {Number} rowIndex
41044          * @param {Number} cellIndex
41045          * @param {Roo.EventObject} e
41046          */
41047          "cellcontextmenu" : true,
41048         /**
41049          * @event headercontextmenu
41050          * Fires when a header is right clicked
41051          * @param {Grid} this
41052          * @param {Number} columnIndex
41053          * @param {Roo.EventObject} e
41054          */
41055         "headercontextmenu" : true,
41056         /**
41057          * @event bodyscroll
41058          * Fires when the body element is scrolled
41059          * @param {Number} scrollLeft
41060          * @param {Number} scrollTop
41061          */
41062         "bodyscroll" : true,
41063         /**
41064          * @event columnresize
41065          * Fires when the user resizes a column
41066          * @param {Number} columnIndex
41067          * @param {Number} newSize
41068          */
41069         "columnresize" : true,
41070         /**
41071          * @event columnmove
41072          * Fires when the user moves a column
41073          * @param {Number} oldIndex
41074          * @param {Number} newIndex
41075          */
41076         "columnmove" : true,
41077         /**
41078          * @event startdrag
41079          * Fires when row(s) start being dragged
41080          * @param {Grid} this
41081          * @param {Roo.GridDD} dd The drag drop object
41082          * @param {event} e The raw browser event
41083          */
41084         "startdrag" : true,
41085         /**
41086          * @event enddrag
41087          * Fires when a drag operation is complete
41088          * @param {Grid} this
41089          * @param {Roo.GridDD} dd The drag drop object
41090          * @param {event} e The raw browser event
41091          */
41092         "enddrag" : true,
41093         /**
41094          * @event dragdrop
41095          * Fires when dragged row(s) are dropped on a valid DD target
41096          * @param {Grid} this
41097          * @param {Roo.GridDD} dd The drag drop object
41098          * @param {String} targetId The target drag drop object
41099          * @param {event} e The raw browser event
41100          */
41101         "dragdrop" : true,
41102         /**
41103          * @event dragover
41104          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41105          * @param {Grid} this
41106          * @param {Roo.GridDD} dd The drag drop object
41107          * @param {String} targetId The target drag drop object
41108          * @param {event} e The raw browser event
41109          */
41110         "dragover" : true,
41111         /**
41112          * @event dragenter
41113          *  Fires when the dragged row(s) first cross another DD target while being dragged
41114          * @param {Grid} this
41115          * @param {Roo.GridDD} dd The drag drop object
41116          * @param {String} targetId The target drag drop object
41117          * @param {event} e The raw browser event
41118          */
41119         "dragenter" : true,
41120         /**
41121          * @event dragout
41122          * Fires when the dragged row(s) leave another DD target while being dragged
41123          * @param {Grid} this
41124          * @param {Roo.GridDD} dd The drag drop object
41125          * @param {String} targetId The target drag drop object
41126          * @param {event} e The raw browser event
41127          */
41128         "dragout" : true,
41129         /**
41130          * @event rowclass
41131          * Fires when a row is rendered, so you can change add a style to it.
41132          * @param {GridView} gridview   The grid view
41133          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41134          */
41135         'rowclass' : true,
41136
41137         /**
41138          * @event render
41139          * Fires when the grid is rendered
41140          * @param {Grid} grid
41141          */
41142         'render' : true,
41143             /**
41144              * @event select
41145              * Fires when a date is selected
41146              * @param {DatePicker} this
41147              * @param {Date} date The selected date
41148              */
41149         'select': true,
41150         /**
41151              * @event monthchange
41152              * Fires when the displayed month changes 
41153              * @param {DatePicker} this
41154              * @param {Date} date The selected month
41155              */
41156         'monthchange': true,
41157         /**
41158              * @event evententer
41159              * Fires when mouse over an event
41160              * @param {Calendar} this
41161              * @param {event} Event
41162              */
41163         'evententer': true,
41164         /**
41165              * @event eventleave
41166              * Fires when the mouse leaves an
41167              * @param {Calendar} this
41168              * @param {event}
41169              */
41170         'eventleave': true,
41171         /**
41172              * @event eventclick
41173              * Fires when the mouse click an
41174              * @param {Calendar} this
41175              * @param {event}
41176              */
41177         'eventclick': true,
41178         /**
41179              * @event eventrender
41180              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41181              * @param {Calendar} this
41182              * @param {data} data to be modified
41183              */
41184         'eventrender': true
41185         
41186     });
41187
41188     Roo.grid.Grid.superclass.constructor.call(this);
41189     this.on('render', function() {
41190         this.view.el.addClass('x-grid-cal'); 
41191         
41192         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41193
41194     },this);
41195     
41196     if (!Roo.grid.Calendar.style) {
41197         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41198             
41199             
41200             '.x-grid-cal .x-grid-col' :  {
41201                 height: 'auto !important',
41202                 'vertical-align': 'top'
41203             },
41204             '.x-grid-cal  .fc-event-hori' : {
41205                 height: '14px'
41206             }
41207              
41208             
41209         }, Roo.id());
41210     }
41211
41212     
41213     
41214 };
41215 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41216     /**
41217      * @cfg {Store} eventStore The store that loads events.
41218      */
41219     eventStore : 25,
41220
41221      
41222     activeDate : false,
41223     startDay : 0,
41224     autoWidth : true,
41225     monitorWindowResize : false,
41226
41227     
41228     resizeColumns : function() {
41229         var col = (this.view.el.getWidth() / 7) - 3;
41230         // loop through cols, and setWidth
41231         for(var i =0 ; i < 7 ; i++){
41232             this.cm.setColumnWidth(i, col);
41233         }
41234     },
41235      setDate :function(date) {
41236         
41237         Roo.log('setDate?');
41238         
41239         this.resizeColumns();
41240         var vd = this.activeDate;
41241         this.activeDate = date;
41242 //        if(vd && this.el){
41243 //            var t = date.getTime();
41244 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41245 //                Roo.log('using add remove');
41246 //                
41247 //                this.fireEvent('monthchange', this, date);
41248 //                
41249 //                this.cells.removeClass("fc-state-highlight");
41250 //                this.cells.each(function(c){
41251 //                   if(c.dateValue == t){
41252 //                       c.addClass("fc-state-highlight");
41253 //                       setTimeout(function(){
41254 //                            try{c.dom.firstChild.focus();}catch(e){}
41255 //                       }, 50);
41256 //                       return false;
41257 //                   }
41258 //                   return true;
41259 //                });
41260 //                return;
41261 //            }
41262 //        }
41263         
41264         var days = date.getDaysInMonth();
41265         
41266         var firstOfMonth = date.getFirstDateOfMonth();
41267         var startingPos = firstOfMonth.getDay()-this.startDay;
41268         
41269         if(startingPos < this.startDay){
41270             startingPos += 7;
41271         }
41272         
41273         var pm = date.add(Date.MONTH, -1);
41274         var prevStart = pm.getDaysInMonth()-startingPos;
41275 //        
41276         
41277         
41278         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41279         
41280         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41281         //this.cells.addClassOnOver('fc-state-hover');
41282         
41283         var cells = this.cells.elements;
41284         var textEls = this.textNodes;
41285         
41286         //Roo.each(cells, function(cell){
41287         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41288         //});
41289         
41290         days += startingPos;
41291
41292         // convert everything to numbers so it's fast
41293         var day = 86400000;
41294         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41295         //Roo.log(d);
41296         //Roo.log(pm);
41297         //Roo.log(prevStart);
41298         
41299         var today = new Date().clearTime().getTime();
41300         var sel = date.clearTime().getTime();
41301         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41302         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41303         var ddMatch = this.disabledDatesRE;
41304         var ddText = this.disabledDatesText;
41305         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41306         var ddaysText = this.disabledDaysText;
41307         var format = this.format;
41308         
41309         var setCellClass = function(cal, cell){
41310             
41311             //Roo.log('set Cell Class');
41312             cell.title = "";
41313             var t = d.getTime();
41314             
41315             //Roo.log(d);
41316             
41317             
41318             cell.dateValue = t;
41319             if(t == today){
41320                 cell.className += " fc-today";
41321                 cell.className += " fc-state-highlight";
41322                 cell.title = cal.todayText;
41323             }
41324             if(t == sel){
41325                 // disable highlight in other month..
41326                 cell.className += " fc-state-highlight";
41327                 
41328             }
41329             // disabling
41330             if(t < min) {
41331                 //cell.className = " fc-state-disabled";
41332                 cell.title = cal.minText;
41333                 return;
41334             }
41335             if(t > max) {
41336                 //cell.className = " fc-state-disabled";
41337                 cell.title = cal.maxText;
41338                 return;
41339             }
41340             if(ddays){
41341                 if(ddays.indexOf(d.getDay()) != -1){
41342                     // cell.title = ddaysText;
41343                    // cell.className = " fc-state-disabled";
41344                 }
41345             }
41346             if(ddMatch && format){
41347                 var fvalue = d.dateFormat(format);
41348                 if(ddMatch.test(fvalue)){
41349                     cell.title = ddText.replace("%0", fvalue);
41350                    cell.className = " fc-state-disabled";
41351                 }
41352             }
41353             
41354             if (!cell.initialClassName) {
41355                 cell.initialClassName = cell.dom.className;
41356             }
41357             
41358             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41359         };
41360
41361         var i = 0;
41362         
41363         for(; i < startingPos; i++) {
41364             cells[i].dayName =  (++prevStart);
41365             Roo.log(textEls[i]);
41366             d.setDate(d.getDate()+1);
41367             
41368             //cells[i].className = "fc-past fc-other-month";
41369             setCellClass(this, cells[i]);
41370         }
41371         
41372         var intDay = 0;
41373         
41374         for(; i < days; i++){
41375             intDay = i - startingPos + 1;
41376             cells[i].dayName =  (intDay);
41377             d.setDate(d.getDate()+1);
41378             
41379             cells[i].className = ''; // "x-date-active";
41380             setCellClass(this, cells[i]);
41381         }
41382         var extraDays = 0;
41383         
41384         for(; i < 42; i++) {
41385             //textEls[i].innerHTML = (++extraDays);
41386             
41387             d.setDate(d.getDate()+1);
41388             cells[i].dayName = (++extraDays);
41389             cells[i].className = "fc-future fc-other-month";
41390             setCellClass(this, cells[i]);
41391         }
41392         
41393         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41394         
41395         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41396         
41397         // this will cause all the cells to mis
41398         var rows= [];
41399         var i =0;
41400         for (var r = 0;r < 6;r++) {
41401             for (var c =0;c < 7;c++) {
41402                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41403             }    
41404         }
41405         
41406         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41407         for(i=0;i<cells.length;i++) {
41408             
41409             this.cells.elements[i].dayName = cells[i].dayName ;
41410             this.cells.elements[i].className = cells[i].className;
41411             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41412             this.cells.elements[i].title = cells[i].title ;
41413             this.cells.elements[i].dateValue = cells[i].dateValue ;
41414         }
41415         
41416         
41417         
41418         
41419         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41420         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41421         
41422         ////if(totalRows != 6){
41423             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41424            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41425        // }
41426         
41427         this.fireEvent('monthchange', this, date);
41428         
41429         
41430     },
41431  /**
41432      * Returns the grid's SelectionModel.
41433      * @return {SelectionModel}
41434      */
41435     getSelectionModel : function(){
41436         if(!this.selModel){
41437             this.selModel = new Roo.grid.CellSelectionModel();
41438         }
41439         return this.selModel;
41440     },
41441
41442     load: function() {
41443         this.eventStore.load()
41444         
41445         
41446         
41447     },
41448     
41449     findCell : function(dt) {
41450         dt = dt.clearTime().getTime();
41451         var ret = false;
41452         this.cells.each(function(c){
41453             //Roo.log("check " +c.dateValue + '?=' + dt);
41454             if(c.dateValue == dt){
41455                 ret = c;
41456                 return false;
41457             }
41458             return true;
41459         });
41460         
41461         return ret;
41462     },
41463     
41464     findCells : function(rec) {
41465         var s = rec.data.start_dt.clone().clearTime().getTime();
41466        // Roo.log(s);
41467         var e= rec.data.end_dt.clone().clearTime().getTime();
41468        // Roo.log(e);
41469         var ret = [];
41470         this.cells.each(function(c){
41471              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41472             
41473             if(c.dateValue > e){
41474                 return ;
41475             }
41476             if(c.dateValue < s){
41477                 return ;
41478             }
41479             ret.push(c);
41480         });
41481         
41482         return ret;    
41483     },
41484     
41485     findBestRow: function(cells)
41486     {
41487         var ret = 0;
41488         
41489         for (var i =0 ; i < cells.length;i++) {
41490             ret  = Math.max(cells[i].rows || 0,ret);
41491         }
41492         return ret;
41493         
41494     },
41495     
41496     
41497     addItem : function(rec)
41498     {
41499         // look for vertical location slot in
41500         var cells = this.findCells(rec);
41501         
41502         rec.row = this.findBestRow(cells);
41503         
41504         // work out the location.
41505         
41506         var crow = false;
41507         var rows = [];
41508         for(var i =0; i < cells.length; i++) {
41509             if (!crow) {
41510                 crow = {
41511                     start : cells[i],
41512                     end :  cells[i]
41513                 };
41514                 continue;
41515             }
41516             if (crow.start.getY() == cells[i].getY()) {
41517                 // on same row.
41518                 crow.end = cells[i];
41519                 continue;
41520             }
41521             // different row.
41522             rows.push(crow);
41523             crow = {
41524                 start: cells[i],
41525                 end : cells[i]
41526             };
41527             
41528         }
41529         
41530         rows.push(crow);
41531         rec.els = [];
41532         rec.rows = rows;
41533         rec.cells = cells;
41534         for (var i = 0; i < cells.length;i++) {
41535             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41536             
41537         }
41538         
41539         
41540     },
41541     
41542     clearEvents: function() {
41543         
41544         if (!this.eventStore.getCount()) {
41545             return;
41546         }
41547         // reset number of rows in cells.
41548         Roo.each(this.cells.elements, function(c){
41549             c.rows = 0;
41550         });
41551         
41552         this.eventStore.each(function(e) {
41553             this.clearEvent(e);
41554         },this);
41555         
41556     },
41557     
41558     clearEvent : function(ev)
41559     {
41560         if (ev.els) {
41561             Roo.each(ev.els, function(el) {
41562                 el.un('mouseenter' ,this.onEventEnter, this);
41563                 el.un('mouseleave' ,this.onEventLeave, this);
41564                 el.remove();
41565             },this);
41566             ev.els = [];
41567         }
41568     },
41569     
41570     
41571     renderEvent : function(ev,ctr) {
41572         if (!ctr) {
41573              ctr = this.view.el.select('.fc-event-container',true).first();
41574         }
41575         
41576          
41577         this.clearEvent(ev);
41578             //code
41579        
41580         
41581         
41582         ev.els = [];
41583         var cells = ev.cells;
41584         var rows = ev.rows;
41585         this.fireEvent('eventrender', this, ev);
41586         
41587         for(var i =0; i < rows.length; i++) {
41588             
41589             cls = '';
41590             if (i == 0) {
41591                 cls += ' fc-event-start';
41592             }
41593             if ((i+1) == rows.length) {
41594                 cls += ' fc-event-end';
41595             }
41596             
41597             //Roo.log(ev.data);
41598             // how many rows should it span..
41599             var cg = this.eventTmpl.append(ctr,Roo.apply({
41600                 fccls : cls
41601                 
41602             }, ev.data) , true);
41603             
41604             
41605             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41606             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41607             cg.on('click', this.onEventClick, this, ev);
41608             
41609             ev.els.push(cg);
41610             
41611             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41612             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41613             //Roo.log(cg);
41614              
41615             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41616             cg.setWidth(ebox.right - sbox.x -2);
41617         }
41618     },
41619     
41620     renderEvents: function()
41621     {   
41622         // first make sure there is enough space..
41623         
41624         if (!this.eventTmpl) {
41625             this.eventTmpl = new Roo.Template(
41626                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41627                     '<div class="fc-event-inner">' +
41628                         '<span class="fc-event-time">{time}</span>' +
41629                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41630                     '</div>' +
41631                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41632                 '</div>'
41633             );
41634                 
41635         }
41636                
41637         
41638         
41639         this.cells.each(function(c) {
41640             //Roo.log(c.select('.fc-day-content div',true).first());
41641             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41642         });
41643         
41644         var ctr = this.view.el.select('.fc-event-container',true).first();
41645         
41646         var cls;
41647         this.eventStore.each(function(ev){
41648             
41649             this.renderEvent(ev);
41650              
41651              
41652         }, this);
41653         this.view.layout();
41654         
41655     },
41656     
41657     onEventEnter: function (e, el,event,d) {
41658         this.fireEvent('evententer', this, el, event);
41659     },
41660     
41661     onEventLeave: function (e, el,event,d) {
41662         this.fireEvent('eventleave', this, el, event);
41663     },
41664     
41665     onEventClick: function (e, el,event,d) {
41666         this.fireEvent('eventclick', this, el, event);
41667     },
41668     
41669     onMonthChange: function () {
41670         this.store.load();
41671     },
41672     
41673     onLoad: function () {
41674         
41675         //Roo.log('calendar onload');
41676 //         
41677         if(this.eventStore.getCount() > 0){
41678             
41679            
41680             
41681             this.eventStore.each(function(d){
41682                 
41683                 
41684                 // FIXME..
41685                 var add =   d.data;
41686                 if (typeof(add.end_dt) == 'undefined')  {
41687                     Roo.log("Missing End time in calendar data: ");
41688                     Roo.log(d);
41689                     return;
41690                 }
41691                 if (typeof(add.start_dt) == 'undefined')  {
41692                     Roo.log("Missing Start time in calendar data: ");
41693                     Roo.log(d);
41694                     return;
41695                 }
41696                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41697                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41698                 add.id = add.id || d.id;
41699                 add.title = add.title || '??';
41700                 
41701                 this.addItem(d);
41702                 
41703              
41704             },this);
41705         }
41706         
41707         this.renderEvents();
41708     }
41709     
41710
41711 });
41712 /*
41713  grid : {
41714                 xtype: 'Grid',
41715                 xns: Roo.grid,
41716                 listeners : {
41717                     render : function ()
41718                     {
41719                         _this.grid = this;
41720                         
41721                         if (!this.view.el.hasClass('course-timesheet')) {
41722                             this.view.el.addClass('course-timesheet');
41723                         }
41724                         if (this.tsStyle) {
41725                             this.ds.load({});
41726                             return; 
41727                         }
41728                         Roo.log('width');
41729                         Roo.log(_this.grid.view.el.getWidth());
41730                         
41731                         
41732                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41733                             '.course-timesheet .x-grid-row' : {
41734                                 height: '80px'
41735                             },
41736                             '.x-grid-row td' : {
41737                                 'vertical-align' : 0
41738                             },
41739                             '.course-edit-link' : {
41740                                 'color' : 'blue',
41741                                 'text-overflow' : 'ellipsis',
41742                                 'overflow' : 'hidden',
41743                                 'white-space' : 'nowrap',
41744                                 'cursor' : 'pointer'
41745                             },
41746                             '.sub-link' : {
41747                                 'color' : 'green'
41748                             },
41749                             '.de-act-sup-link' : {
41750                                 'color' : 'purple',
41751                                 'text-decoration' : 'line-through'
41752                             },
41753                             '.de-act-link' : {
41754                                 'color' : 'red',
41755                                 'text-decoration' : 'line-through'
41756                             },
41757                             '.course-timesheet .course-highlight' : {
41758                                 'border-top-style': 'dashed !important',
41759                                 'border-bottom-bottom': 'dashed !important'
41760                             },
41761                             '.course-timesheet .course-item' : {
41762                                 'font-family'   : 'tahoma, arial, helvetica',
41763                                 'font-size'     : '11px',
41764                                 'overflow'      : 'hidden',
41765                                 'padding-left'  : '10px',
41766                                 'padding-right' : '10px',
41767                                 'padding-top' : '10px' 
41768                             }
41769                             
41770                         }, Roo.id());
41771                                 this.ds.load({});
41772                     }
41773                 },
41774                 autoWidth : true,
41775                 monitorWindowResize : false,
41776                 cellrenderer : function(v,x,r)
41777                 {
41778                     return v;
41779                 },
41780                 sm : {
41781                     xtype: 'CellSelectionModel',
41782                     xns: Roo.grid
41783                 },
41784                 dataSource : {
41785                     xtype: 'Store',
41786                     xns: Roo.data,
41787                     listeners : {
41788                         beforeload : function (_self, options)
41789                         {
41790                             options.params = options.params || {};
41791                             options.params._month = _this.monthField.getValue();
41792                             options.params.limit = 9999;
41793                             options.params['sort'] = 'when_dt';    
41794                             options.params['dir'] = 'ASC';    
41795                             this.proxy.loadResponse = this.loadResponse;
41796                             Roo.log("load?");
41797                             //this.addColumns();
41798                         },
41799                         load : function (_self, records, options)
41800                         {
41801                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41802                                 // if you click on the translation.. you can edit it...
41803                                 var el = Roo.get(this);
41804                                 var id = el.dom.getAttribute('data-id');
41805                                 var d = el.dom.getAttribute('data-date');
41806                                 var t = el.dom.getAttribute('data-time');
41807                                 //var id = this.child('span').dom.textContent;
41808                                 
41809                                 //Roo.log(this);
41810                                 Pman.Dialog.CourseCalendar.show({
41811                                     id : id,
41812                                     when_d : d,
41813                                     when_t : t,
41814                                     productitem_active : id ? 1 : 0
41815                                 }, function() {
41816                                     _this.grid.ds.load({});
41817                                 });
41818                            
41819                            });
41820                            
41821                            _this.panel.fireEvent('resize', [ '', '' ]);
41822                         }
41823                     },
41824                     loadResponse : function(o, success, response){
41825                             // this is overridden on before load..
41826                             
41827                             Roo.log("our code?");       
41828                             //Roo.log(success);
41829                             //Roo.log(response)
41830                             delete this.activeRequest;
41831                             if(!success){
41832                                 this.fireEvent("loadexception", this, o, response);
41833                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41834                                 return;
41835                             }
41836                             var result;
41837                             try {
41838                                 result = o.reader.read(response);
41839                             }catch(e){
41840                                 Roo.log("load exception?");
41841                                 this.fireEvent("loadexception", this, o, response, e);
41842                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41843                                 return;
41844                             }
41845                             Roo.log("ready...");        
41846                             // loop through result.records;
41847                             // and set this.tdate[date] = [] << array of records..
41848                             _this.tdata  = {};
41849                             Roo.each(result.records, function(r){
41850                                 //Roo.log(r.data);
41851                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41852                                     _this.tdata[r.data.when_dt.format('j')] = [];
41853                                 }
41854                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41855                             });
41856                             
41857                             //Roo.log(_this.tdata);
41858                             
41859                             result.records = [];
41860                             result.totalRecords = 6;
41861                     
41862                             // let's generate some duumy records for the rows.
41863                             //var st = _this.dateField.getValue();
41864                             
41865                             // work out monday..
41866                             //st = st.add(Date.DAY, -1 * st.format('w'));
41867                             
41868                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41869                             
41870                             var firstOfMonth = date.getFirstDayOfMonth();
41871                             var days = date.getDaysInMonth();
41872                             var d = 1;
41873                             var firstAdded = false;
41874                             for (var i = 0; i < result.totalRecords ; i++) {
41875                                 //var d= st.add(Date.DAY, i);
41876                                 var row = {};
41877                                 var added = 0;
41878                                 for(var w = 0 ; w < 7 ; w++){
41879                                     if(!firstAdded && firstOfMonth != w){
41880                                         continue;
41881                                     }
41882                                     if(d > days){
41883                                         continue;
41884                                     }
41885                                     firstAdded = true;
41886                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41887                                     row['weekday'+w] = String.format(
41888                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41889                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41890                                                     d,
41891                                                     date.format('Y-m-')+dd
41892                                                 );
41893                                     added++;
41894                                     if(typeof(_this.tdata[d]) != 'undefined'){
41895                                         Roo.each(_this.tdata[d], function(r){
41896                                             var is_sub = '';
41897                                             var deactive = '';
41898                                             var id = r.id;
41899                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41900                                             if(r.parent_id*1>0){
41901                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41902                                                 id = r.parent_id;
41903                                             }
41904                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41905                                                 deactive = 'de-act-link';
41906                                             }
41907                                             
41908                                             row['weekday'+w] += String.format(
41909                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41910                                                     id, //0
41911                                                     r.product_id_name, //1
41912                                                     r.when_dt.format('h:ia'), //2
41913                                                     is_sub, //3
41914                                                     deactive, //4
41915                                                     desc // 5
41916                                             );
41917                                         });
41918                                     }
41919                                     d++;
41920                                 }
41921                                 
41922                                 // only do this if something added..
41923                                 if(added > 0){ 
41924                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41925                                 }
41926                                 
41927                                 
41928                                 // push it twice. (second one with an hour..
41929                                 
41930                             }
41931                             //Roo.log(result);
41932                             this.fireEvent("load", this, o, o.request.arg);
41933                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41934                         },
41935                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41936                     proxy : {
41937                         xtype: 'HttpProxy',
41938                         xns: Roo.data,
41939                         method : 'GET',
41940                         url : baseURL + '/Roo/Shop_course.php'
41941                     },
41942                     reader : {
41943                         xtype: 'JsonReader',
41944                         xns: Roo.data,
41945                         id : 'id',
41946                         fields : [
41947                             {
41948                                 'name': 'id',
41949                                 'type': 'int'
41950                             },
41951                             {
41952                                 'name': 'when_dt',
41953                                 'type': 'string'
41954                             },
41955                             {
41956                                 'name': 'end_dt',
41957                                 'type': 'string'
41958                             },
41959                             {
41960                                 'name': 'parent_id',
41961                                 'type': 'int'
41962                             },
41963                             {
41964                                 'name': 'product_id',
41965                                 'type': 'int'
41966                             },
41967                             {
41968                                 'name': 'productitem_id',
41969                                 'type': 'int'
41970                             },
41971                             {
41972                                 'name': 'guid',
41973                                 'type': 'int'
41974                             }
41975                         ]
41976                     }
41977                 },
41978                 toolbar : {
41979                     xtype: 'Toolbar',
41980                     xns: Roo,
41981                     items : [
41982                         {
41983                             xtype: 'Button',
41984                             xns: Roo.Toolbar,
41985                             listeners : {
41986                                 click : function (_self, e)
41987                                 {
41988                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41989                                     sd.setMonth(sd.getMonth()-1);
41990                                     _this.monthField.setValue(sd.format('Y-m-d'));
41991                                     _this.grid.ds.load({});
41992                                 }
41993                             },
41994                             text : "Back"
41995                         },
41996                         {
41997                             xtype: 'Separator',
41998                             xns: Roo.Toolbar
41999                         },
42000                         {
42001                             xtype: 'MonthField',
42002                             xns: Roo.form,
42003                             listeners : {
42004                                 render : function (_self)
42005                                 {
42006                                     _this.monthField = _self;
42007                                    // _this.monthField.set  today
42008                                 },
42009                                 select : function (combo, date)
42010                                 {
42011                                     _this.grid.ds.load({});
42012                                 }
42013                             },
42014                             value : (function() { return new Date(); })()
42015                         },
42016                         {
42017                             xtype: 'Separator',
42018                             xns: Roo.Toolbar
42019                         },
42020                         {
42021                             xtype: 'TextItem',
42022                             xns: Roo.Toolbar,
42023                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42024                         },
42025                         {
42026                             xtype: 'Fill',
42027                             xns: Roo.Toolbar
42028                         },
42029                         {
42030                             xtype: 'Button',
42031                             xns: Roo.Toolbar,
42032                             listeners : {
42033                                 click : function (_self, e)
42034                                 {
42035                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42036                                     sd.setMonth(sd.getMonth()+1);
42037                                     _this.monthField.setValue(sd.format('Y-m-d'));
42038                                     _this.grid.ds.load({});
42039                                 }
42040                             },
42041                             text : "Next"
42042                         }
42043                     ]
42044                 },
42045                  
42046             }
42047         };
42048         
42049         *//*
42050  * Based on:
42051  * Ext JS Library 1.1.1
42052  * Copyright(c) 2006-2007, Ext JS, LLC.
42053  *
42054  * Originally Released Under LGPL - original licence link has changed is not relivant.
42055  *
42056  * Fork - LGPL
42057  * <script type="text/javascript">
42058  */
42059  
42060 /**
42061  * @class Roo.LoadMask
42062  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42063  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42064  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42065  * element's UpdateManager load indicator and will be destroyed after the initial load.
42066  * @constructor
42067  * Create a new LoadMask
42068  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42069  * @param {Object} config The config object
42070  */
42071 Roo.LoadMask = function(el, config){
42072     this.el = Roo.get(el);
42073     Roo.apply(this, config);
42074     if(this.store){
42075         this.store.on('beforeload', this.onBeforeLoad, this);
42076         this.store.on('load', this.onLoad, this);
42077         this.store.on('loadexception', this.onLoadException, this);
42078         this.removeMask = false;
42079     }else{
42080         var um = this.el.getUpdateManager();
42081         um.showLoadIndicator = false; // disable the default indicator
42082         um.on('beforeupdate', this.onBeforeLoad, this);
42083         um.on('update', this.onLoad, this);
42084         um.on('failure', this.onLoad, this);
42085         this.removeMask = true;
42086     }
42087 };
42088
42089 Roo.LoadMask.prototype = {
42090     /**
42091      * @cfg {Boolean} removeMask
42092      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42093      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42094      */
42095     /**
42096      * @cfg {String} msg
42097      * The text to display in a centered loading message box (defaults to 'Loading...')
42098      */
42099     msg : 'Loading...',
42100     /**
42101      * @cfg {String} msgCls
42102      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42103      */
42104     msgCls : 'x-mask-loading',
42105
42106     /**
42107      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42108      * @type Boolean
42109      */
42110     disabled: false,
42111
42112     /**
42113      * Disables the mask to prevent it from being displayed
42114      */
42115     disable : function(){
42116        this.disabled = true;
42117     },
42118
42119     /**
42120      * Enables the mask so that it can be displayed
42121      */
42122     enable : function(){
42123         this.disabled = false;
42124     },
42125     
42126     onLoadException : function()
42127     {
42128         Roo.log(arguments);
42129         
42130         if (typeof(arguments[3]) != 'undefined') {
42131             Roo.MessageBox.alert("Error loading",arguments[3]);
42132         } 
42133         /*
42134         try {
42135             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42136                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42137             }   
42138         } catch(e) {
42139             
42140         }
42141         */
42142     
42143         
42144         
42145         this.el.unmask(this.removeMask);
42146     },
42147     // private
42148     onLoad : function()
42149     {
42150         this.el.unmask(this.removeMask);
42151     },
42152
42153     // private
42154     onBeforeLoad : function(){
42155         if(!this.disabled){
42156             this.el.mask(this.msg, this.msgCls);
42157         }
42158     },
42159
42160     // private
42161     destroy : function(){
42162         if(this.store){
42163             this.store.un('beforeload', this.onBeforeLoad, this);
42164             this.store.un('load', this.onLoad, this);
42165             this.store.un('loadexception', this.onLoadException, this);
42166         }else{
42167             var um = this.el.getUpdateManager();
42168             um.un('beforeupdate', this.onBeforeLoad, this);
42169             um.un('update', this.onLoad, this);
42170             um.un('failure', this.onLoad, this);
42171         }
42172     }
42173 };/*
42174  * Based on:
42175  * Ext JS Library 1.1.1
42176  * Copyright(c) 2006-2007, Ext JS, LLC.
42177  *
42178  * Originally Released Under LGPL - original licence link has changed is not relivant.
42179  *
42180  * Fork - LGPL
42181  * <script type="text/javascript">
42182  */
42183
42184
42185 /**
42186  * @class Roo.XTemplate
42187  * @extends Roo.Template
42188  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42189 <pre><code>
42190 var t = new Roo.XTemplate(
42191         '&lt;select name="{name}"&gt;',
42192                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42193         '&lt;/select&gt;'
42194 );
42195  
42196 // then append, applying the master template values
42197  </code></pre>
42198  *
42199  * Supported features:
42200  *
42201  *  Tags:
42202
42203 <pre><code>
42204       {a_variable} - output encoded.
42205       {a_variable.format:("Y-m-d")} - call a method on the variable
42206       {a_variable:raw} - unencoded output
42207       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42208       {a_variable:this.method_on_template(...)} - call a method on the template object.
42209  
42210 </code></pre>
42211  *  The tpl tag:
42212 <pre><code>
42213         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42214         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42215         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42216         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42217   
42218         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42219         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42220 </code></pre>
42221  *      
42222  */
42223 Roo.XTemplate = function()
42224 {
42225     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42226     if (this.html) {
42227         this.compile();
42228     }
42229 };
42230
42231
42232 Roo.extend(Roo.XTemplate, Roo.Template, {
42233
42234     /**
42235      * The various sub templates
42236      */
42237     tpls : false,
42238     /**
42239      *
42240      * basic tag replacing syntax
42241      * WORD:WORD()
42242      *
42243      * // you can fake an object call by doing this
42244      *  x.t:(test,tesT) 
42245      * 
42246      */
42247     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42248
42249     /**
42250      * compile the template
42251      *
42252      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42253      *
42254      */
42255     compile: function()
42256     {
42257         var s = this.html;
42258      
42259         s = ['<tpl>', s, '</tpl>'].join('');
42260     
42261         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42262             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42263             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42264             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42265             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42266             m,
42267             id     = 0,
42268             tpls   = [];
42269     
42270         while(true == !!(m = s.match(re))){
42271             var forMatch   = m[0].match(nameRe),
42272                 ifMatch   = m[0].match(ifRe),
42273                 execMatch   = m[0].match(execRe),
42274                 namedMatch   = m[0].match(namedRe),
42275                 
42276                 exp  = null, 
42277                 fn   = null,
42278                 exec = null,
42279                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42280                 
42281             if (ifMatch) {
42282                 // if - puts fn into test..
42283                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42284                 if(exp){
42285                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42286                 }
42287             }
42288             
42289             if (execMatch) {
42290                 // exec - calls a function... returns empty if true is  returned.
42291                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42292                 if(exp){
42293                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42294                 }
42295             }
42296             
42297             
42298             if (name) {
42299                 // for = 
42300                 switch(name){
42301                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42302                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42303                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42304                 }
42305             }
42306             var uid = namedMatch ? namedMatch[1] : id;
42307             
42308             
42309             tpls.push({
42310                 id:     namedMatch ? namedMatch[1] : id,
42311                 target: name,
42312                 exec:   exec,
42313                 test:   fn,
42314                 body:   m[1] || ''
42315             });
42316             if (namedMatch) {
42317                 s = s.replace(m[0], '');
42318             } else { 
42319                 s = s.replace(m[0], '{xtpl'+ id + '}');
42320             }
42321             ++id;
42322         }
42323         this.tpls = [];
42324         for(var i = tpls.length-1; i >= 0; --i){
42325             this.compileTpl(tpls[i]);
42326             this.tpls[tpls[i].id] = tpls[i];
42327         }
42328         this.master = tpls[tpls.length-1];
42329         return this;
42330     },
42331     /**
42332      * same as applyTemplate, except it's done to one of the subTemplates
42333      * when using named templates, you can do:
42334      *
42335      * var str = pl.applySubTemplate('your-name', values);
42336      *
42337      * 
42338      * @param {Number} id of the template
42339      * @param {Object} values to apply to template
42340      * @param {Object} parent (normaly the instance of this object)
42341      */
42342     applySubTemplate : function(id, values, parent)
42343     {
42344         
42345         
42346         var t = this.tpls[id];
42347         
42348         
42349         try { 
42350             if(t.test && !t.test.call(this, values, parent)){
42351                 return '';
42352             }
42353         } catch(e) {
42354             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42355             Roo.log(e.toString());
42356             Roo.log(t.test);
42357             return ''
42358         }
42359         try { 
42360             
42361             if(t.exec && t.exec.call(this, values, parent)){
42362                 return '';
42363             }
42364         } catch(e) {
42365             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42366             Roo.log(e.toString());
42367             Roo.log(t.exec);
42368             return ''
42369         }
42370         try {
42371             var vs = t.target ? t.target.call(this, values, parent) : values;
42372             parent = t.target ? values : parent;
42373             if(t.target && vs instanceof Array){
42374                 var buf = [];
42375                 for(var i = 0, len = vs.length; i < len; i++){
42376                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42377                 }
42378                 return buf.join('');
42379             }
42380             return t.compiled.call(this, vs, parent);
42381         } catch (e) {
42382             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42383             Roo.log(e.toString());
42384             Roo.log(t.compiled);
42385             return '';
42386         }
42387     },
42388
42389     compileTpl : function(tpl)
42390     {
42391         var fm = Roo.util.Format;
42392         var useF = this.disableFormats !== true;
42393         var sep = Roo.isGecko ? "+" : ",";
42394         var undef = function(str) {
42395             Roo.log("Property not found :"  + str);
42396             return '';
42397         };
42398         
42399         var fn = function(m, name, format, args)
42400         {
42401             //Roo.log(arguments);
42402             args = args ? args.replace(/\\'/g,"'") : args;
42403             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42404             if (typeof(format) == 'undefined') {
42405                 format= 'htmlEncode';
42406             }
42407             if (format == 'raw' ) {
42408                 format = false;
42409             }
42410             
42411             if(name.substr(0, 4) == 'xtpl'){
42412                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42413             }
42414             
42415             // build an array of options to determine if value is undefined..
42416             
42417             // basically get 'xxxx.yyyy' then do
42418             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42419             //    (function () { Roo.log("Property not found"); return ''; })() :
42420             //    ......
42421             
42422             var udef_ar = [];
42423             var lookfor = '';
42424             Roo.each(name.split('.'), function(st) {
42425                 lookfor += (lookfor.length ? '.': '') + st;
42426                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42427             });
42428             
42429             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42430             
42431             
42432             if(format && useF){
42433                 
42434                 args = args ? ',' + args : "";
42435                  
42436                 if(format.substr(0, 5) != "this."){
42437                     format = "fm." + format + '(';
42438                 }else{
42439                     format = 'this.call("'+ format.substr(5) + '", ';
42440                     args = ", values";
42441                 }
42442                 
42443                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42444             }
42445              
42446             if (args.length) {
42447                 // called with xxyx.yuu:(test,test)
42448                 // change to ()
42449                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42450             }
42451             // raw.. - :raw modifier..
42452             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42453             
42454         };
42455         var body;
42456         // branched to use + in gecko and [].join() in others
42457         if(Roo.isGecko){
42458             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42459                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42460                     "';};};";
42461         }else{
42462             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42463             body.push(tpl.body.replace(/(\r\n|\n)/g,
42464                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42465             body.push("'].join('');};};");
42466             body = body.join('');
42467         }
42468         
42469         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42470        
42471         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42472         eval(body);
42473         
42474         return this;
42475     },
42476
42477     applyTemplate : function(values){
42478         return this.master.compiled.call(this, values, {});
42479         //var s = this.subs;
42480     },
42481
42482     apply : function(){
42483         return this.applyTemplate.apply(this, arguments);
42484     }
42485
42486  });
42487
42488 Roo.XTemplate.from = function(el){
42489     el = Roo.getDom(el);
42490     return new Roo.XTemplate(el.value || el.innerHTML);
42491 };