89dc69d6abe1155d96b84672b5b0f781f4e92c12
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) {
4462             val = 0;
4463         }
4464         return val;
4465     },
4466     
4467     /**
4468      * Integer sorting
4469      * @param {Mixed} s The value being converted
4470      * @return {Number} The comparison value
4471      */
4472     asInt : function(s) {
4473         var val = parseInt(String(s).replace(/,/g, ""));
4474         if(isNaN(val)) {
4475             val = 0;
4476         }
4477         return val;
4478     }
4479 };/*
4480  * Based on:
4481  * Ext JS Library 1.1.1
4482  * Copyright(c) 2006-2007, Ext JS, LLC.
4483  *
4484  * Originally Released Under LGPL - original licence link has changed is not relivant.
4485  *
4486  * Fork - LGPL
4487  * <script type="text/javascript">
4488  */
4489
4490 /**
4491 * @class Roo.data.Record
4492  * Instances of this class encapsulate both record <em>definition</em> information, and record
4493  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4494  * to access Records cached in an {@link Roo.data.Store} object.<br>
4495  * <p>
4496  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4497  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4498  * objects.<br>
4499  * <p>
4500  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4501  * @constructor
4502  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4503  * {@link #create}. The parameters are the same.
4504  * @param {Array} data An associative Array of data values keyed by the field name.
4505  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4506  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4507  * not specified an integer id is generated.
4508  */
4509 Roo.data.Record = function(data, id){
4510     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4511     this.data = data;
4512 };
4513
4514 /**
4515  * Generate a constructor for a specific record layout.
4516  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4517  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4518  * Each field definition object may contain the following properties: <ul>
4519  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4520  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4521  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4522  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4523  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4524  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4525  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4526  * this may be omitted.</p></li>
4527  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4528  * <ul><li>auto (Default, implies no conversion)</li>
4529  * <li>string</li>
4530  * <li>int</li>
4531  * <li>float</li>
4532  * <li>boolean</li>
4533  * <li>date</li></ul></p></li>
4534  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4535  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4536  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4537  * by the Reader into an object that will be stored in the Record. It is passed the
4538  * following parameters:<ul>
4539  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4540  * </ul></p></li>
4541  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4542  * </ul>
4543  * <br>usage:<br><pre><code>
4544 var TopicRecord = Roo.data.Record.create(
4545     {name: 'title', mapping: 'topic_title'},
4546     {name: 'author', mapping: 'username'},
4547     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4548     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4549     {name: 'lastPoster', mapping: 'user2'},
4550     {name: 'excerpt', mapping: 'post_text'}
4551 );
4552
4553 var myNewRecord = new TopicRecord({
4554     title: 'Do my job please',
4555     author: 'noobie',
4556     totalPosts: 1,
4557     lastPost: new Date(),
4558     lastPoster: 'Animal',
4559     excerpt: 'No way dude!'
4560 });
4561 myStore.add(myNewRecord);
4562 </code></pre>
4563  * @method create
4564  * @static
4565  */
4566 Roo.data.Record.create = function(o){
4567     var f = function(){
4568         f.superclass.constructor.apply(this, arguments);
4569     };
4570     Roo.extend(f, Roo.data.Record);
4571     var p = f.prototype;
4572     p.fields = new Roo.util.MixedCollection(false, function(field){
4573         return field.name;
4574     });
4575     for(var i = 0, len = o.length; i < len; i++){
4576         p.fields.add(new Roo.data.Field(o[i]));
4577     }
4578     f.getField = function(name){
4579         return p.fields.get(name);  
4580     };
4581     return f;
4582 };
4583
4584 Roo.data.Record.AUTO_ID = 1000;
4585 Roo.data.Record.EDIT = 'edit';
4586 Roo.data.Record.REJECT = 'reject';
4587 Roo.data.Record.COMMIT = 'commit';
4588
4589 Roo.data.Record.prototype = {
4590     /**
4591      * Readonly flag - true if this record has been modified.
4592      * @type Boolean
4593      */
4594     dirty : false,
4595     editing : false,
4596     error: null,
4597     modified: null,
4598
4599     // private
4600     join : function(store){
4601         this.store = store;
4602     },
4603
4604     /**
4605      * Set the named field to the specified value.
4606      * @param {String} name The name of the field to set.
4607      * @param {Object} value The value to set the field to.
4608      */
4609     set : function(name, value){
4610         if(this.data[name] == value){
4611             return;
4612         }
4613         this.dirty = true;
4614         if(!this.modified){
4615             this.modified = {};
4616         }
4617         if(typeof this.modified[name] == 'undefined'){
4618             this.modified[name] = this.data[name];
4619         }
4620         this.data[name] = value;
4621         if(!this.editing && this.store){
4622             this.store.afterEdit(this);
4623         }       
4624     },
4625
4626     /**
4627      * Get the value of the named field.
4628      * @param {String} name The name of the field to get the value of.
4629      * @return {Object} The value of the field.
4630      */
4631     get : function(name){
4632         return this.data[name]; 
4633     },
4634
4635     // private
4636     beginEdit : function(){
4637         this.editing = true;
4638         this.modified = {}; 
4639     },
4640
4641     // private
4642     cancelEdit : function(){
4643         this.editing = false;
4644         delete this.modified;
4645     },
4646
4647     // private
4648     endEdit : function(){
4649         this.editing = false;
4650         if(this.dirty && this.store){
4651             this.store.afterEdit(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Rejects all changes made to the Record since either creation, or the last commit operation.
4658      * Modified fields are reverted to their original values.
4659      * <p>
4660      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4661      * of reject operations.
4662      */
4663     reject : function(){
4664         var m = this.modified;
4665         for(var n in m){
4666             if(typeof m[n] != "function"){
4667                 this.data[n] = m[n];
4668             }
4669         }
4670         this.dirty = false;
4671         delete this.modified;
4672         this.editing = false;
4673         if(this.store){
4674             this.store.afterReject(this);
4675         }
4676     },
4677
4678     /**
4679      * Usually called by the {@link Roo.data.Store} which owns the Record.
4680      * Commits all changes made to the Record since either creation, or the last commit operation.
4681      * <p>
4682      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4683      * of commit operations.
4684      */
4685     commit : function(){
4686         this.dirty = false;
4687         delete this.modified;
4688         this.editing = false;
4689         if(this.store){
4690             this.store.afterCommit(this);
4691         }
4692     },
4693
4694     // private
4695     hasError : function(){
4696         return this.error != null;
4697     },
4698
4699     // private
4700     clearError : function(){
4701         this.error = null;
4702     },
4703
4704     /**
4705      * Creates a copy of this record.
4706      * @param {String} id (optional) A new record id if you don't want to use this record's id
4707      * @return {Record}
4708      */
4709     copy : function(newId) {
4710         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4711     }
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722
4723
4724
4725 /**
4726  * @class Roo.data.Store
4727  * @extends Roo.util.Observable
4728  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4729  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4730  * <p>
4731  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4732  * has no knowledge of the format of the data returned by the Proxy.<br>
4733  * <p>
4734  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4735  * instances from the data object. These records are cached and made available through accessor functions.
4736  * @constructor
4737  * Creates a new Store.
4738  * @param {Object} config A config object containing the objects needed for the Store to access data,
4739  * and read the data into Records.
4740  */
4741 Roo.data.Store = function(config){
4742     this.data = new Roo.util.MixedCollection(false);
4743     this.data.getKey = function(o){
4744         return o.id;
4745     };
4746     this.baseParams = {};
4747     // private
4748     this.paramNames = {
4749         "start" : "start",
4750         "limit" : "limit",
4751         "sort" : "sort",
4752         "dir" : "dir",
4753         "multisort" : "_multisort"
4754     };
4755
4756     if(config && config.data){
4757         this.inlineData = config.data;
4758         delete config.data;
4759     }
4760
4761     Roo.apply(this, config);
4762     
4763     if(this.reader){ // reader passed
4764         this.reader = Roo.factory(this.reader, Roo.data);
4765         this.reader.xmodule = this.xmodule || false;
4766         if(!this.recordType){
4767             this.recordType = this.reader.recordType;
4768         }
4769         if(this.reader.onMetaChange){
4770             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4771         }
4772     }
4773
4774     if(this.recordType){
4775         this.fields = this.recordType.prototype.fields;
4776     }
4777     this.modified = [];
4778
4779     this.addEvents({
4780         /**
4781          * @event datachanged
4782          * Fires when the data cache has changed, and a widget which is using this Store
4783          * as a Record cache should refresh its view.
4784          * @param {Store} this
4785          */
4786         datachanged : true,
4787         /**
4788          * @event metachange
4789          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4790          * @param {Store} this
4791          * @param {Object} meta The JSON metadata
4792          */
4793         metachange : true,
4794         /**
4795          * @event add
4796          * Fires when Records have been added to the Store
4797          * @param {Store} this
4798          * @param {Roo.data.Record[]} records The array of Records added
4799          * @param {Number} index The index at which the record(s) were added
4800          */
4801         add : true,
4802         /**
4803          * @event remove
4804          * Fires when a Record has been removed from the Store
4805          * @param {Store} this
4806          * @param {Roo.data.Record} record The Record that was removed
4807          * @param {Number} index The index at which the record was removed
4808          */
4809         remove : true,
4810         /**
4811          * @event update
4812          * Fires when a Record has been updated
4813          * @param {Store} this
4814          * @param {Roo.data.Record} record The Record that was updated
4815          * @param {String} operation The update operation being performed.  Value may be one of:
4816          * <pre><code>
4817  Roo.data.Record.EDIT
4818  Roo.data.Record.REJECT
4819  Roo.data.Record.COMMIT
4820          * </code></pre>
4821          */
4822         update : true,
4823         /**
4824          * @event clear
4825          * Fires when the data cache has been cleared.
4826          * @param {Store} this
4827          */
4828         clear : true,
4829         /**
4830          * @event beforeload
4831          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4832          * the load action will be canceled.
4833          * @param {Store} this
4834          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4835          */
4836         beforeload : true,
4837         /**
4838          * @event beforeloadadd
4839          * Fires after a new set of Records has been loaded.
4840          * @param {Store} this
4841          * @param {Roo.data.Record[]} records The Records that were loaded
4842          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4843          */
4844         beforeloadadd : true,
4845         /**
4846          * @event load
4847          * Fires after a new set of Records has been loaded, before they are added to the store.
4848          * @param {Store} this
4849          * @param {Roo.data.Record[]} records The Records that were loaded
4850          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4851          * @params {Object} return from reader
4852          */
4853         load : true,
4854         /**
4855          * @event loadexception
4856          * Fires if an exception occurs in the Proxy during loading.
4857          * Called with the signature of the Proxy's "loadexception" event.
4858          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4859          * 
4860          * @param {Proxy} 
4861          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4862          * @param {Object} load options 
4863          * @param {Object} jsonData from your request (normally this contains the Exception)
4864          */
4865         loadexception : true
4866     });
4867     
4868     if(this.proxy){
4869         this.proxy = Roo.factory(this.proxy, Roo.data);
4870         this.proxy.xmodule = this.xmodule || false;
4871         this.relayEvents(this.proxy,  ["loadexception"]);
4872     }
4873     this.sortToggle = {};
4874     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4875
4876     Roo.data.Store.superclass.constructor.call(this);
4877
4878     if(this.inlineData){
4879         this.loadData(this.inlineData);
4880         delete this.inlineData;
4881     }
4882 };
4883
4884 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4885      /**
4886     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4887     * without a remote query - used by combo/forms at present.
4888     */
4889     
4890     /**
4891     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4892     */
4893     /**
4894     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4895     */
4896     /**
4897     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4898     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4899     */
4900     /**
4901     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4902     * on any HTTP request
4903     */
4904     /**
4905     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4906     */
4907     /**
4908     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4909     */
4910     multiSort: false,
4911     /**
4912     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4913     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4914     */
4915     remoteSort : false,
4916
4917     /**
4918     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4919      * loaded or when a record is removed. (defaults to false).
4920     */
4921     pruneModifiedRecords : false,
4922
4923     // private
4924     lastOptions : null,
4925
4926     /**
4927      * Add Records to the Store and fires the add event.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     add : function(records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             records[i].join(this);
4934         }
4935         var index = this.data.length;
4936         this.data.addAll(records);
4937         this.fireEvent("add", this, records, index);
4938     },
4939
4940     /**
4941      * Remove a Record from the Store and fires the remove event.
4942      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4943      */
4944     remove : function(record){
4945         var index = this.data.indexOf(record);
4946         this.data.removeAt(index);
4947         if(this.pruneModifiedRecords){
4948             this.modified.remove(record);
4949         }
4950         this.fireEvent("remove", this, record, index);
4951     },
4952
4953     /**
4954      * Remove all Records from the Store and fires the clear event.
4955      */
4956     removeAll : function(){
4957         this.data.clear();
4958         if(this.pruneModifiedRecords){
4959             this.modified = [];
4960         }
4961         this.fireEvent("clear", this);
4962     },
4963
4964     /**
4965      * Inserts Records to the Store at the given index and fires the add event.
4966      * @param {Number} index The start index at which to insert the passed Records.
4967      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4968      */
4969     insert : function(index, records){
4970         records = [].concat(records);
4971         for(var i = 0, len = records.length; i < len; i++){
4972             this.data.insert(index, records[i]);
4973             records[i].join(this);
4974         }
4975         this.fireEvent("add", this, records, index);
4976     },
4977
4978     /**
4979      * Get the index within the cache of the passed Record.
4980      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4981      * @return {Number} The index of the passed Record. Returns -1 if not found.
4982      */
4983     indexOf : function(record){
4984         return this.data.indexOf(record);
4985     },
4986
4987     /**
4988      * Get the index within the cache of the Record with the passed id.
4989      * @param {String} id The id of the Record to find.
4990      * @return {Number} The index of the Record. Returns -1 if not found.
4991      */
4992     indexOfId : function(id){
4993         return this.data.indexOfKey(id);
4994     },
4995
4996     /**
4997      * Get the Record with the specified id.
4998      * @param {String} id The id of the Record to find.
4999      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5000      */
5001     getById : function(id){
5002         return this.data.key(id);
5003     },
5004
5005     /**
5006      * Get the Record at the specified index.
5007      * @param {Number} index The index of the Record to find.
5008      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5009      */
5010     getAt : function(index){
5011         return this.data.itemAt(index);
5012     },
5013
5014     /**
5015      * Returns a range of Records between specified indices.
5016      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5017      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5018      * @return {Roo.data.Record[]} An array of Records
5019      */
5020     getRange : function(start, end){
5021         return this.data.getRange(start, end);
5022     },
5023
5024     // private
5025     storeOptions : function(o){
5026         o = Roo.apply({}, o);
5027         delete o.callback;
5028         delete o.scope;
5029         this.lastOptions = o;
5030     },
5031
5032     /**
5033      * Loads the Record cache from the configured Proxy using the configured Reader.
5034      * <p>
5035      * If using remote paging, then the first load call must specify the <em>start</em>
5036      * and <em>limit</em> properties in the options.params property to establish the initial
5037      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5038      * <p>
5039      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5040      * and this call will return before the new data has been loaded. Perform any post-processing
5041      * in a callback function, or in a "load" event handler.</strong>
5042      * <p>
5043      * @param {Object} options An object containing properties which control loading options:<ul>
5044      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5045      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5046      * passed the following arguments:<ul>
5047      * <li>r : Roo.data.Record[]</li>
5048      * <li>options: Options object from the load call</li>
5049      * <li>success: Boolean success indicator</li></ul></li>
5050      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5051      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5052      * </ul>
5053      */
5054     load : function(options){
5055         options = options || {};
5056         if(this.fireEvent("beforeload", this, options) !== false){
5057             this.storeOptions(options);
5058             var p = Roo.apply(options.params || {}, this.baseParams);
5059             // if meta was not loaded from remote source.. try requesting it.
5060             if (!this.reader.metaFromRemote) {
5061                 p._requestMeta = 1;
5062             }
5063             if(this.sortInfo && this.remoteSort){
5064                 var pn = this.paramNames;
5065                 p[pn["sort"]] = this.sortInfo.field;
5066                 p[pn["dir"]] = this.sortInfo.direction;
5067             }
5068             if (this.multiSort) {
5069                 var pn = this.paramNames;
5070                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5071             }
5072             
5073             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5074         }
5075     },
5076
5077     /**
5078      * Reloads the Record cache from the configured Proxy using the configured Reader and
5079      * the options from the last load operation performed.
5080      * @param {Object} options (optional) An object containing properties which may override the options
5081      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5082      * the most recently used options are reused).
5083      */
5084     reload : function(options){
5085         this.load(Roo.applyIf(options||{}, this.lastOptions));
5086     },
5087
5088     // private
5089     // Called as a callback by the Reader during a load operation.
5090     loadRecords : function(o, options, success){
5091         if(!o || success === false){
5092             if(success !== false){
5093                 this.fireEvent("load", this, [], options, o);
5094             }
5095             if(options.callback){
5096                 options.callback.call(options.scope || this, [], options, false);
5097             }
5098             return;
5099         }
5100         // if data returned failure - throw an exception.
5101         if (o.success === false) {
5102             // show a message if no listener is registered.
5103             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5104                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5105             }
5106             // loadmask wil be hooked into this..
5107             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5108             return;
5109         }
5110         var r = o.records, t = o.totalRecords || r.length;
5111         
5112         this.fireEvent("beforeloadadd", this, r, options, o);
5113         
5114         if(!options || options.add !== true){
5115             if(this.pruneModifiedRecords){
5116                 this.modified = [];
5117             }
5118             for(var i = 0, len = r.length; i < len; i++){
5119                 r[i].join(this);
5120             }
5121             if(this.snapshot){
5122                 this.data = this.snapshot;
5123                 delete this.snapshot;
5124             }
5125             this.data.clear();
5126             this.data.addAll(r);
5127             this.totalLength = t;
5128             this.applySort();
5129             this.fireEvent("datachanged", this);
5130         }else{
5131             this.totalLength = Math.max(t, this.data.length+r.length);
5132             this.add(r);
5133         }
5134         this.fireEvent("load", this, r, options, o);
5135         if(options.callback){
5136             options.callback.call(options.scope || this, r, options, true);
5137         }
5138     },
5139
5140
5141     /**
5142      * Loads data from a passed data block. A Reader which understands the format of the data
5143      * must have been configured in the constructor.
5144      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5145      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5146      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5147      */
5148     loadData : function(o, append){
5149         var r = this.reader.readRecords(o);
5150         this.loadRecords(r, {add: append}, true);
5151     },
5152
5153     /**
5154      * Gets the number of cached records.
5155      * <p>
5156      * <em>If using paging, this may not be the total size of the dataset. If the data object
5157      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5158      * the data set size</em>
5159      */
5160     getCount : function(){
5161         return this.data.length || 0;
5162     },
5163
5164     /**
5165      * Gets the total number of records in the dataset as returned by the server.
5166      * <p>
5167      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5168      * the dataset size</em>
5169      */
5170     getTotalCount : function(){
5171         return this.totalLength || 0;
5172     },
5173
5174     /**
5175      * Returns the sort state of the Store as an object with two properties:
5176      * <pre><code>
5177  field {String} The name of the field by which the Records are sorted
5178  direction {String} The sort order, "ASC" or "DESC"
5179      * </code></pre>
5180      */
5181     getSortState : function(){
5182         return this.sortInfo;
5183     },
5184
5185     // private
5186     applySort : function(){
5187         if(this.sortInfo && !this.remoteSort){
5188             var s = this.sortInfo, f = s.field;
5189             var st = this.fields.get(f).sortType;
5190             var fn = function(r1, r2){
5191                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5192                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5193             };
5194             this.data.sort(s.direction, fn);
5195             if(this.snapshot && this.snapshot != this.data){
5196                 this.snapshot.sort(s.direction, fn);
5197             }
5198         }
5199     },
5200
5201     /**
5202      * Sets the default sort column and order to be used by the next load operation.
5203      * @param {String} fieldName The name of the field to sort by.
5204      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5205      */
5206     setDefaultSort : function(field, dir){
5207         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5208     },
5209
5210     /**
5211      * Sort the Records.
5212      * If remote sorting is used, the sort is performed on the server, and the cache is
5213      * reloaded. If local sorting is used, the cache is sorted internally.
5214      * @param {String} fieldName The name of the field to sort by.
5215      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5216      */
5217     sort : function(fieldName, dir){
5218         var f = this.fields.get(fieldName);
5219         if(!dir){
5220             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5221             
5222             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5223                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5224             }else{
5225                 dir = f.sortDir;
5226             }
5227         }
5228         this.sortToggle[f.name] = dir;
5229         this.sortInfo = {field: f.name, direction: dir};
5230         if(!this.remoteSort){
5231             this.applySort();
5232             this.fireEvent("datachanged", this);
5233         }else{
5234             this.load(this.lastOptions);
5235         }
5236     },
5237
5238     /**
5239      * Calls the specified function for each of the Records in the cache.
5240      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5241      * Returning <em>false</em> aborts and exits the iteration.
5242      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5243      */
5244     each : function(fn, scope){
5245         this.data.each(fn, scope);
5246     },
5247
5248     /**
5249      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5250      * (e.g., during paging).
5251      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5252      */
5253     getModifiedRecords : function(){
5254         return this.modified;
5255     },
5256
5257     // private
5258     createFilterFn : function(property, value, anyMatch){
5259         if(!value.exec){ // not a regex
5260             value = String(value);
5261             if(value.length == 0){
5262                 return false;
5263             }
5264             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5265         }
5266         return function(r){
5267             return value.test(r.data[property]);
5268         };
5269     },
5270
5271     /**
5272      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5273      * @param {String} property A field on your records
5274      * @param {Number} start The record index to start at (defaults to 0)
5275      * @param {Number} end The last record index to include (defaults to length - 1)
5276      * @return {Number} The sum
5277      */
5278     sum : function(property, start, end){
5279         var rs = this.data.items, v = 0;
5280         start = start || 0;
5281         end = (end || end === 0) ? end : rs.length-1;
5282
5283         for(var i = start; i <= end; i++){
5284             v += (rs[i].data[property] || 0);
5285         }
5286         return v;
5287     },
5288
5289     /**
5290      * Filter the records by a specified property.
5291      * @param {String} field A field on your records
5292      * @param {String/RegExp} value Either a string that the field
5293      * should start with or a RegExp to test against the field
5294      * @param {Boolean} anyMatch True to match any part not just the beginning
5295      */
5296     filter : function(property, value, anyMatch){
5297         var fn = this.createFilterFn(property, value, anyMatch);
5298         return fn ? this.filterBy(fn) : this.clearFilter();
5299     },
5300
5301     /**
5302      * Filter by a function. The specified function will be called with each
5303      * record in this data source. If the function returns true the record is included,
5304      * otherwise it is filtered.
5305      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5306      * @param {Object} scope (optional) The scope of the function (defaults to this)
5307      */
5308     filterBy : function(fn, scope){
5309         this.snapshot = this.snapshot || this.data;
5310         this.data = this.queryBy(fn, scope||this);
5311         this.fireEvent("datachanged", this);
5312     },
5313
5314     /**
5315      * Query the records by a specified property.
5316      * @param {String} field A field on your records
5317      * @param {String/RegExp} value Either a string that the field
5318      * should start with or a RegExp to test against the field
5319      * @param {Boolean} anyMatch True to match any part not just the beginning
5320      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5321      */
5322     query : function(property, value, anyMatch){
5323         var fn = this.createFilterFn(property, value, anyMatch);
5324         return fn ? this.queryBy(fn) : this.data.clone();
5325     },
5326
5327     /**
5328      * Query by a function. The specified function will be called with each
5329      * record in this data source. If the function returns true the record is included
5330      * in the results.
5331      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5332      * @param {Object} scope (optional) The scope of the function (defaults to this)
5333       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5334      **/
5335     queryBy : function(fn, scope){
5336         var data = this.snapshot || this.data;
5337         return data.filterBy(fn, scope||this);
5338     },
5339
5340     /**
5341      * Collects unique values for a particular dataIndex from this store.
5342      * @param {String} dataIndex The property to collect
5343      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5344      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5345      * @return {Array} An array of the unique values
5346      **/
5347     collect : function(dataIndex, allowNull, bypassFilter){
5348         var d = (bypassFilter === true && this.snapshot) ?
5349                 this.snapshot.items : this.data.items;
5350         var v, sv, r = [], l = {};
5351         for(var i = 0, len = d.length; i < len; i++){
5352             v = d[i].data[dataIndex];
5353             sv = String(v);
5354             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5355                 l[sv] = true;
5356                 r[r.length] = v;
5357             }
5358         }
5359         return r;
5360     },
5361
5362     /**
5363      * Revert to a view of the Record cache with no filtering applied.
5364      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5365      */
5366     clearFilter : function(suppressEvent){
5367         if(this.snapshot && this.snapshot != this.data){
5368             this.data = this.snapshot;
5369             delete this.snapshot;
5370             if(suppressEvent !== true){
5371                 this.fireEvent("datachanged", this);
5372             }
5373         }
5374     },
5375
5376     // private
5377     afterEdit : function(record){
5378         if(this.modified.indexOf(record) == -1){
5379             this.modified.push(record);
5380         }
5381         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5382     },
5383     
5384     // private
5385     afterReject : function(record){
5386         this.modified.remove(record);
5387         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5388     },
5389
5390     // private
5391     afterCommit : function(record){
5392         this.modified.remove(record);
5393         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5394     },
5395
5396     /**
5397      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5398      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5399      */
5400     commitChanges : function(){
5401         var m = this.modified.slice(0);
5402         this.modified = [];
5403         for(var i = 0, len = m.length; i < len; i++){
5404             m[i].commit();
5405         }
5406     },
5407
5408     /**
5409      * Cancel outstanding changes on all changed records.
5410      */
5411     rejectChanges : function(){
5412         var m = this.modified.slice(0);
5413         this.modified = [];
5414         for(var i = 0, len = m.length; i < len; i++){
5415             m[i].reject();
5416         }
5417     },
5418
5419     onMetaChange : function(meta, rtype, o){
5420         this.recordType = rtype;
5421         this.fields = rtype.prototype.fields;
5422         delete this.snapshot;
5423         this.sortInfo = meta.sortInfo || this.sortInfo;
5424         this.modified = [];
5425         this.fireEvent('metachange', this, this.reader.meta);
5426     },
5427     
5428     moveIndex : function(data, type)
5429     {
5430         var index = this.indexOf(data);
5431         
5432         var newIndex = index + type;
5433         
5434         this.remove(data);
5435         
5436         this.insert(newIndex, data);
5437         
5438     }
5439 });/*
5440  * Based on:
5441  * Ext JS Library 1.1.1
5442  * Copyright(c) 2006-2007, Ext JS, LLC.
5443  *
5444  * Originally Released Under LGPL - original licence link has changed is not relivant.
5445  *
5446  * Fork - LGPL
5447  * <script type="text/javascript">
5448  */
5449
5450 /**
5451  * @class Roo.data.SimpleStore
5452  * @extends Roo.data.Store
5453  * Small helper class to make creating Stores from Array data easier.
5454  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5455  * @cfg {Array} fields An array of field definition objects, or field name strings.
5456  * @cfg {Array} data The multi-dimensional array of data
5457  * @constructor
5458  * @param {Object} config
5459  */
5460 Roo.data.SimpleStore = function(config){
5461     Roo.data.SimpleStore.superclass.constructor.call(this, {
5462         isLocal : true,
5463         reader: new Roo.data.ArrayReader({
5464                 id: config.id
5465             },
5466             Roo.data.Record.create(config.fields)
5467         ),
5468         proxy : new Roo.data.MemoryProxy(config.data)
5469     });
5470     this.load();
5471 };
5472 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483 /**
5484 /**
5485  * @extends Roo.data.Store
5486  * @class Roo.data.JsonStore
5487  * Small helper class to make creating Stores for JSON data easier. <br/>
5488 <pre><code>
5489 var store = new Roo.data.JsonStore({
5490     url: 'get-images.php',
5491     root: 'images',
5492     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5493 });
5494 </code></pre>
5495  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5496  * JsonReader and HttpProxy (unless inline data is provided).</b>
5497  * @cfg {Array} fields An array of field definition objects, or field name strings.
5498  * @constructor
5499  * @param {Object} config
5500  */
5501 Roo.data.JsonStore = function(c){
5502     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5503         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5504         reader: new Roo.data.JsonReader(c, c.fields)
5505     }));
5506 };
5507 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5508  * Based on:
5509  * Ext JS Library 1.1.1
5510  * Copyright(c) 2006-2007, Ext JS, LLC.
5511  *
5512  * Originally Released Under LGPL - original licence link has changed is not relivant.
5513  *
5514  * Fork - LGPL
5515  * <script type="text/javascript">
5516  */
5517
5518  
5519 Roo.data.Field = function(config){
5520     if(typeof config == "string"){
5521         config = {name: config};
5522     }
5523     Roo.apply(this, config);
5524     
5525     if(!this.type){
5526         this.type = "auto";
5527     }
5528     
5529     var st = Roo.data.SortTypes;
5530     // named sortTypes are supported, here we look them up
5531     if(typeof this.sortType == "string"){
5532         this.sortType = st[this.sortType];
5533     }
5534     
5535     // set default sortType for strings and dates
5536     if(!this.sortType){
5537         switch(this.type){
5538             case "string":
5539                 this.sortType = st.asUCString;
5540                 break;
5541             case "date":
5542                 this.sortType = st.asDate;
5543                 break;
5544             default:
5545                 this.sortType = st.none;
5546         }
5547     }
5548
5549     // define once
5550     var stripRe = /[\$,%]/g;
5551
5552     // prebuilt conversion function for this field, instead of
5553     // switching every time we're reading a value
5554     if(!this.convert){
5555         var cv, dateFormat = this.dateFormat;
5556         switch(this.type){
5557             case "":
5558             case "auto":
5559             case undefined:
5560                 cv = function(v){ return v; };
5561                 break;
5562             case "string":
5563                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5564                 break;
5565             case "int":
5566                 cv = function(v){
5567                     return v !== undefined && v !== null && v !== '' ?
5568                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5569                     };
5570                 break;
5571             case "float":
5572                 cv = function(v){
5573                     return v !== undefined && v !== null && v !== '' ?
5574                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5575                     };
5576                 break;
5577             case "bool":
5578             case "boolean":
5579                 cv = function(v){ return v === true || v === "true" || v == 1; };
5580                 break;
5581             case "date":
5582                 cv = function(v){
5583                     if(!v){
5584                         return '';
5585                     }
5586                     if(v instanceof Date){
5587                         return v;
5588                     }
5589                     if(dateFormat){
5590                         if(dateFormat == "timestamp"){
5591                             return new Date(v*1000);
5592                         }
5593                         return Date.parseDate(v, dateFormat);
5594                     }
5595                     var parsed = Date.parse(v);
5596                     return parsed ? new Date(parsed) : null;
5597                 };
5598              break;
5599             
5600         }
5601         this.convert = cv;
5602     }
5603 };
5604
5605 Roo.data.Field.prototype = {
5606     dateFormat: null,
5607     defaultValue: "",
5608     mapping: null,
5609     sortType : null,
5610     sortDir : "ASC"
5611 };/*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621  
5622 // Base class for reading structured data from a data source.  This class is intended to be
5623 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5624
5625 /**
5626  * @class Roo.data.DataReader
5627  * Base class for reading structured data from a data source.  This class is intended to be
5628  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5629  */
5630
5631 Roo.data.DataReader = function(meta, recordType){
5632     
5633     this.meta = meta;
5634     
5635     this.recordType = recordType instanceof Array ? 
5636         Roo.data.Record.create(recordType) : recordType;
5637 };
5638
5639 Roo.data.DataReader.prototype = {
5640      /**
5641      * Create an empty record
5642      * @param {Object} data (optional) - overlay some values
5643      * @return {Roo.data.Record} record created.
5644      */
5645     newRow :  function(d) {
5646         var da =  {};
5647         this.recordType.prototype.fields.each(function(c) {
5648             switch( c.type) {
5649                 case 'int' : da[c.name] = 0; break;
5650                 case 'date' : da[c.name] = new Date(); break;
5651                 case 'float' : da[c.name] = 0.0; break;
5652                 case 'boolean' : da[c.name] = false; break;
5653                 default : da[c.name] = ""; break;
5654             }
5655             
5656         });
5657         return new this.recordType(Roo.apply(da, d));
5658     }
5659     
5660 };/*
5661  * Based on:
5662  * Ext JS Library 1.1.1
5663  * Copyright(c) 2006-2007, Ext JS, LLC.
5664  *
5665  * Originally Released Under LGPL - original licence link has changed is not relivant.
5666  *
5667  * Fork - LGPL
5668  * <script type="text/javascript">
5669  */
5670
5671 /**
5672  * @class Roo.data.DataProxy
5673  * @extends Roo.data.Observable
5674  * This class is an abstract base class for implementations which provide retrieval of
5675  * unformatted data objects.<br>
5676  * <p>
5677  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5678  * (of the appropriate type which knows how to parse the data object) to provide a block of
5679  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5680  * <p>
5681  * Custom implementations must implement the load method as described in
5682  * {@link Roo.data.HttpProxy#load}.
5683  */
5684 Roo.data.DataProxy = function(){
5685     this.addEvents({
5686         /**
5687          * @event beforeload
5688          * Fires before a network request is made to retrieve a data object.
5689          * @param {Object} This DataProxy object.
5690          * @param {Object} params The params parameter to the load function.
5691          */
5692         beforeload : true,
5693         /**
5694          * @event load
5695          * Fires before the load method's callback is called.
5696          * @param {Object} This DataProxy object.
5697          * @param {Object} o The data object.
5698          * @param {Object} arg The callback argument object passed to the load function.
5699          */
5700         load : true,
5701         /**
5702          * @event loadexception
5703          * Fires if an Exception occurs during data retrieval.
5704          * @param {Object} This DataProxy object.
5705          * @param {Object} o The data object.
5706          * @param {Object} arg The callback argument object passed to the load function.
5707          * @param {Object} e The Exception.
5708          */
5709         loadexception : true
5710     });
5711     Roo.data.DataProxy.superclass.constructor.call(this);
5712 };
5713
5714 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5715
5716     /**
5717      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5718      */
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.MemoryProxy
5731  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5732  * to the Reader when its load method is called.
5733  * @constructor
5734  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5735  */
5736 Roo.data.MemoryProxy = function(data){
5737     if (data.data) {
5738         data = data.data;
5739     }
5740     Roo.data.MemoryProxy.superclass.constructor.call(this);
5741     this.data = data;
5742 };
5743
5744 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5745     /**
5746      * Load data from the requested source (in this case an in-memory
5747      * data object passed to the constructor), read the data object into
5748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5749      * process that block using the passed callback.
5750      * @param {Object} params This parameter is not used by the MemoryProxy class.
5751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5752      * object into a block of Roo.data.Records.
5753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5754      * The function must be passed <ul>
5755      * <li>The Record block object</li>
5756      * <li>The "arg" argument from the load function</li>
5757      * <li>A boolean success indicator</li>
5758      * </ul>
5759      * @param {Object} scope The scope in which to call the callback
5760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5761      */
5762     load : function(params, reader, callback, scope, arg){
5763         params = params || {};
5764         var result;
5765         try {
5766             result = reader.readRecords(this.data);
5767         }catch(e){
5768             this.fireEvent("loadexception", this, arg, null, e);
5769             callback.call(scope, null, arg, false);
5770             return;
5771         }
5772         callback.call(scope, result, arg, true);
5773     },
5774     
5775     // private
5776     update : function(params, records){
5777         
5778     }
5779 });/*
5780  * Based on:
5781  * Ext JS Library 1.1.1
5782  * Copyright(c) 2006-2007, Ext JS, LLC.
5783  *
5784  * Originally Released Under LGPL - original licence link has changed is not relivant.
5785  *
5786  * Fork - LGPL
5787  * <script type="text/javascript">
5788  */
5789 /**
5790  * @class Roo.data.HttpProxy
5791  * @extends Roo.data.DataProxy
5792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5793  * configured to reference a certain URL.<br><br>
5794  * <p>
5795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5796  * from which the running page was served.<br><br>
5797  * <p>
5798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5799  * <p>
5800  * Be aware that to enable the browser to parse an XML document, the server must set
5801  * the Content-Type header in the HTTP response to "text/xml".
5802  * @constructor
5803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5805  * will be used to make the request.
5806  */
5807 Roo.data.HttpProxy = function(conn){
5808     Roo.data.HttpProxy.superclass.constructor.call(this);
5809     // is conn a conn config or a real conn?
5810     this.conn = conn;
5811     this.useAjax = !conn || !conn.events;
5812   
5813 };
5814
5815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5816     // thse are take from connection...
5817     
5818     /**
5819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5823      * extra parameters to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5827      *  to each request made by this object. (defaults to undefined)
5828      */
5829     /**
5830      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5834      */
5835      /**
5836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5837      * @type Boolean
5838      */
5839   
5840
5841     /**
5842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5843      * @type Boolean
5844      */
5845     /**
5846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5848      * a finer-grained basis than the DataProxy events.
5849      */
5850     getConnection : function(){
5851         return this.useAjax ? Roo.Ajax : this.conn;
5852     },
5853
5854     /**
5855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5857      * process that block using the passed callback.
5858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5859      * for the request to the remote server.
5860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5861      * object into a block of Roo.data.Records.
5862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5863      * The function must be passed <ul>
5864      * <li>The Record block object</li>
5865      * <li>The "arg" argument from the load function</li>
5866      * <li>A boolean success indicator</li>
5867      * </ul>
5868      * @param {Object} scope The scope in which to call the callback
5869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5870      */
5871     load : function(params, reader, callback, scope, arg){
5872         if(this.fireEvent("beforeload", this, params) !== false){
5873             var  o = {
5874                 params : params || {},
5875                 request: {
5876                     callback : callback,
5877                     scope : scope,
5878                     arg : arg
5879                 },
5880                 reader: reader,
5881                 callback : this.loadResponse,
5882                 scope: this
5883             };
5884             if(this.useAjax){
5885                 Roo.applyIf(o, this.conn);
5886                 if(this.activeRequest){
5887                     Roo.Ajax.abort(this.activeRequest);
5888                 }
5889                 this.activeRequest = Roo.Ajax.request(o);
5890             }else{
5891                 this.conn.request(o);
5892             }
5893         }else{
5894             callback.call(scope||this, null, arg, false);
5895         }
5896     },
5897
5898     // private
5899     loadResponse : function(o, success, response){
5900         delete this.activeRequest;
5901         if(!success){
5902             this.fireEvent("loadexception", this, o, response);
5903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5904             return;
5905         }
5906         var result;
5907         try {
5908             result = o.reader.read(response);
5909         }catch(e){
5910             this.fireEvent("loadexception", this, o, response, e);
5911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5912             return;
5913         }
5914         
5915         this.fireEvent("load", this, o, o.request.arg);
5916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5917     },
5918
5919     // private
5920     update : function(dataSet){
5921
5922     },
5923
5924     // private
5925     updateResponse : function(dataSet){
5926
5927     }
5928 });/*
5929  * Based on:
5930  * Ext JS Library 1.1.1
5931  * Copyright(c) 2006-2007, Ext JS, LLC.
5932  *
5933  * Originally Released Under LGPL - original licence link has changed is not relivant.
5934  *
5935  * Fork - LGPL
5936  * <script type="text/javascript">
5937  */
5938
5939 /**
5940  * @class Roo.data.ScriptTagProxy
5941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5942  * other than the originating domain of the running page.<br><br>
5943  * <p>
5944  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5946  * <p>
5947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5948  * source code that is used as the source inside a &lt;script> tag.<br><br>
5949  * <p>
5950  * In order for the browser to process the returned data, the server must wrap the data object
5951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5953  * depending on whether the callback name was passed:
5954  * <p>
5955  * <pre><code>
5956 boolean scriptTag = false;
5957 String cb = request.getParameter("callback");
5958 if (cb != null) {
5959     scriptTag = true;
5960     response.setContentType("text/javascript");
5961 } else {
5962     response.setContentType("application/x-json");
5963 }
5964 Writer out = response.getWriter();
5965 if (scriptTag) {
5966     out.write(cb + "(");
5967 }
5968 out.print(dataBlock.toJsonString());
5969 if (scriptTag) {
5970     out.write(");");
5971 }
5972 </pre></code>
5973  *
5974  * @constructor
5975  * @param {Object} config A configuration object.
5976  */
5977 Roo.data.ScriptTagProxy = function(config){
5978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5979     Roo.apply(this, config);
5980     this.head = document.getElementsByTagName("head")[0];
5981 };
5982
5983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5984
5985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5986     /**
5987      * @cfg {String} url The URL from which to request the data object.
5988      */
5989     /**
5990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5991      */
5992     timeout : 30000,
5993     /**
5994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5995      * the server the name of the callback function set up by the load call to process the returned data object.
5996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5997      * javascript output which calls this named function passing the data object as its only parameter.
5998      */
5999     callbackParam : "callback",
6000     /**
6001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6002      * name to the request.
6003      */
6004     nocache : true,
6005
6006     /**
6007      * Load data from the configured URL, read the data object into
6008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6009      * process that block using the passed callback.
6010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6011      * for the request to the remote server.
6012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6013      * object into a block of Roo.data.Records.
6014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6015      * The function must be passed <ul>
6016      * <li>The Record block object</li>
6017      * <li>The "arg" argument from the load function</li>
6018      * <li>A boolean success indicator</li>
6019      * </ul>
6020      * @param {Object} scope The scope in which to call the callback
6021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6022      */
6023     load : function(params, reader, callback, scope, arg){
6024         if(this.fireEvent("beforeload", this, params) !== false){
6025
6026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6027
6028             var url = this.url;
6029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6030             if(this.nocache){
6031                 url += "&_dc=" + (new Date().getTime());
6032             }
6033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6034             var trans = {
6035                 id : transId,
6036                 cb : "stcCallback"+transId,
6037                 scriptId : "stcScript"+transId,
6038                 params : params,
6039                 arg : arg,
6040                 url : url,
6041                 callback : callback,
6042                 scope : scope,
6043                 reader : reader
6044             };
6045             var conn = this;
6046
6047             window[trans.cb] = function(o){
6048                 conn.handleResponse(o, trans);
6049             };
6050
6051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6052
6053             if(this.autoAbort !== false){
6054                 this.abort();
6055             }
6056
6057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6058
6059             var script = document.createElement("script");
6060             script.setAttribute("src", url);
6061             script.setAttribute("type", "text/javascript");
6062             script.setAttribute("id", trans.scriptId);
6063             this.head.appendChild(script);
6064
6065             this.trans = trans;
6066         }else{
6067             callback.call(scope||this, null, arg, false);
6068         }
6069     },
6070
6071     // private
6072     isLoading : function(){
6073         return this.trans ? true : false;
6074     },
6075
6076     /**
6077      * Abort the current server request.
6078      */
6079     abort : function(){
6080         if(this.isLoading()){
6081             this.destroyTrans(this.trans);
6082         }
6083     },
6084
6085     // private
6086     destroyTrans : function(trans, isLoaded){
6087         this.head.removeChild(document.getElementById(trans.scriptId));
6088         clearTimeout(trans.timeoutId);
6089         if(isLoaded){
6090             window[trans.cb] = undefined;
6091             try{
6092                 delete window[trans.cb];
6093             }catch(e){}
6094         }else{
6095             // if hasn't been loaded, wait for load to remove it to prevent script error
6096             window[trans.cb] = function(){
6097                 window[trans.cb] = undefined;
6098                 try{
6099                     delete window[trans.cb];
6100                 }catch(e){}
6101             };
6102         }
6103     },
6104
6105     // private
6106     handleResponse : function(o, trans){
6107         this.trans = false;
6108         this.destroyTrans(trans, true);
6109         var result;
6110         try {
6111             result = trans.reader.readRecords(o);
6112         }catch(e){
6113             this.fireEvent("loadexception", this, o, trans.arg, e);
6114             trans.callback.call(trans.scope||window, null, trans.arg, false);
6115             return;
6116         }
6117         this.fireEvent("load", this, o, trans.arg);
6118         trans.callback.call(trans.scope||window, result, trans.arg, true);
6119     },
6120
6121     // private
6122     handleFailure : function(trans){
6123         this.trans = false;
6124         this.destroyTrans(trans, false);
6125         this.fireEvent("loadexception", this, null, trans.arg);
6126         trans.callback.call(trans.scope||window, null, trans.arg, false);
6127     }
6128 });/*
6129  * Based on:
6130  * Ext JS Library 1.1.1
6131  * Copyright(c) 2006-2007, Ext JS, LLC.
6132  *
6133  * Originally Released Under LGPL - original licence link has changed is not relivant.
6134  *
6135  * Fork - LGPL
6136  * <script type="text/javascript">
6137  */
6138
6139 /**
6140  * @class Roo.data.JsonReader
6141  * @extends Roo.data.DataReader
6142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6143  * based on mappings in a provided Roo.data.Record constructor.
6144  * 
6145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6146  * in the reply previously. 
6147  * 
6148  * <p>
6149  * Example code:
6150  * <pre><code>
6151 var RecordDef = Roo.data.Record.create([
6152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6154 ]);
6155 var myReader = new Roo.data.JsonReader({
6156     totalProperty: "results",    // The property which contains the total dataset size (optional)
6157     root: "rows",                // The property which contains an Array of row objects
6158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6159 }, RecordDef);
6160 </code></pre>
6161  * <p>
6162  * This would consume a JSON file like this:
6163  * <pre><code>
6164 { 'results': 2, 'rows': [
6165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6167 }
6168 </code></pre>
6169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6171  * paged from the remote server.
6172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6173  * @cfg {String} root name of the property which contains the Array of row objects.
6174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6175  * @cfg {Array} fields Array of field definition objects
6176  * @constructor
6177  * Create a new JsonReader
6178  * @param {Object} meta Metadata configuration options
6179  * @param {Object} recordType Either an Array of field definition objects,
6180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6181  */
6182 Roo.data.JsonReader = function(meta, recordType){
6183     
6184     meta = meta || {};
6185     // set some defaults:
6186     Roo.applyIf(meta, {
6187         totalProperty: 'total',
6188         successProperty : 'success',
6189         root : 'data',
6190         id : 'id'
6191     });
6192     
6193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6194 };
6195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6196     
6197     /**
6198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6199      * Used by Store query builder to append _requestMeta to params.
6200      * 
6201      */
6202     metaFromRemote : false,
6203     /**
6204      * This method is only used by a DataProxy which has retrieved data from a remote server.
6205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6206      * @return {Object} data A data block which is used by an Roo.data.Store object as
6207      * a cache of Roo.data.Records.
6208      */
6209     read : function(response){
6210         var json = response.responseText;
6211        
6212         var o = /* eval:var:o */ eval("("+json+")");
6213         if(!o) {
6214             throw {message: "JsonReader.read: Json object not found"};
6215         }
6216         
6217         if(o.metaData){
6218             
6219             delete this.ef;
6220             this.metaFromRemote = true;
6221             this.meta = o.metaData;
6222             this.recordType = Roo.data.Record.create(o.metaData.fields);
6223             this.onMetaChange(this.meta, this.recordType, o);
6224         }
6225         return this.readRecords(o);
6226     },
6227
6228     // private function a store will implement
6229     onMetaChange : function(meta, recordType, o){
6230
6231     },
6232
6233     /**
6234          * @ignore
6235          */
6236     simpleAccess: function(obj, subsc) {
6237         return obj[subsc];
6238     },
6239
6240         /**
6241          * @ignore
6242          */
6243     getJsonAccessor: function(){
6244         var re = /[\[\.]/;
6245         return function(expr) {
6246             try {
6247                 return(re.test(expr))
6248                     ? new Function("obj", "return obj." + expr)
6249                     : function(obj){
6250                         return obj[expr];
6251                     };
6252             } catch(e){}
6253             return Roo.emptyFn;
6254         };
6255     }(),
6256
6257     /**
6258      * Create a data block containing Roo.data.Records from an XML document.
6259      * @param {Object} o An object which contains an Array of row objects in the property specified
6260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6261      * which contains the total size of the dataset.
6262      * @return {Object} data A data block which is used by an Roo.data.Store object as
6263      * a cache of Roo.data.Records.
6264      */
6265     readRecords : function(o){
6266         /**
6267          * After any data loads, the raw JSON data is available for further custom processing.
6268          * @type Object
6269          */
6270         this.o = o;
6271         var s = this.meta, Record = this.recordType,
6272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6273
6274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6275         if (!this.ef) {
6276             if(s.totalProperty) {
6277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6278                 }
6279                 if(s.successProperty) {
6280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6281                 }
6282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6283                 if (s.id) {
6284                         var g = this.getJsonAccessor(s.id);
6285                         this.getId = function(rec) {
6286                                 var r = g(rec);  
6287                                 return (r === undefined || r === "") ? null : r;
6288                         };
6289                 } else {
6290                         this.getId = function(){return null;};
6291                 }
6292             this.ef = [];
6293             for(var jj = 0; jj < fl; jj++){
6294                 f = fi[jj];
6295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6296                 this.ef[jj] = this.getJsonAccessor(map);
6297             }
6298         }
6299
6300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6301         if(s.totalProperty){
6302             var vt = parseInt(this.getTotal(o), 10);
6303             if(!isNaN(vt)){
6304                 totalRecords = vt;
6305             }
6306         }
6307         if(s.successProperty){
6308             var vs = this.getSuccess(o);
6309             if(vs === false || vs === 'false'){
6310                 success = false;
6311             }
6312         }
6313         var records = [];
6314         for(var i = 0; i < c; i++){
6315                 var n = root[i];
6316             var values = {};
6317             var id = this.getId(n);
6318             for(var j = 0; j < fl; j++){
6319                 f = fi[j];
6320             var v = this.ef[j](n);
6321             if (!f.convert) {
6322                 Roo.log('missing convert for ' + f.name);
6323                 Roo.log(f);
6324                 continue;
6325             }
6326             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6327             }
6328             var record = new Record(values, id);
6329             record.json = n;
6330             records[i] = record;
6331         }
6332         return {
6333             raw : o,
6334             success : success,
6335             records : records,
6336             totalRecords : totalRecords
6337         };
6338     }
6339 });/*
6340  * Based on:
6341  * Ext JS Library 1.1.1
6342  * Copyright(c) 2006-2007, Ext JS, LLC.
6343  *
6344  * Originally Released Under LGPL - original licence link has changed is not relivant.
6345  *
6346  * Fork - LGPL
6347  * <script type="text/javascript">
6348  */
6349
6350 /**
6351  * @class Roo.data.XmlReader
6352  * @extends Roo.data.DataReader
6353  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6354  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6355  * <p>
6356  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6357  * header in the HTTP response must be set to "text/xml".</em>
6358  * <p>
6359  * Example code:
6360  * <pre><code>
6361 var RecordDef = Roo.data.Record.create([
6362    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6363    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6364 ]);
6365 var myReader = new Roo.data.XmlReader({
6366    totalRecords: "results", // The element which contains the total dataset size (optional)
6367    record: "row",           // The repeated element which contains row information
6368    id: "id"                 // The element within the row that provides an ID for the record (optional)
6369 }, RecordDef);
6370 </code></pre>
6371  * <p>
6372  * This would consume an XML file like this:
6373  * <pre><code>
6374 &lt;?xml?>
6375 &lt;dataset>
6376  &lt;results>2&lt;/results>
6377  &lt;row>
6378    &lt;id>1&lt;/id>
6379    &lt;name>Bill&lt;/name>
6380    &lt;occupation>Gardener&lt;/occupation>
6381  &lt;/row>
6382  &lt;row>
6383    &lt;id>2&lt;/id>
6384    &lt;name>Ben&lt;/name>
6385    &lt;occupation>Horticulturalist&lt;/occupation>
6386  &lt;/row>
6387 &lt;/dataset>
6388 </code></pre>
6389  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6390  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6391  * paged from the remote server.
6392  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6393  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6394  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6395  * a record identifier value.
6396  * @constructor
6397  * Create a new XmlReader
6398  * @param {Object} meta Metadata configuration options
6399  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6400  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6401  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6402  */
6403 Roo.data.XmlReader = function(meta, recordType){
6404     meta = meta || {};
6405     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6406 };
6407 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6408     /**
6409      * This method is only used by a DataProxy which has retrieved data from a remote server.
6410          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6411          * to contain a method called 'responseXML' that returns an XML document object.
6412      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6413      * a cache of Roo.data.Records.
6414      */
6415     read : function(response){
6416         var doc = response.responseXML;
6417         if(!doc) {
6418             throw {message: "XmlReader.read: XML Document not available"};
6419         }
6420         return this.readRecords(doc);
6421     },
6422
6423     /**
6424      * Create a data block containing Roo.data.Records from an XML document.
6425          * @param {Object} doc A parsed XML document.
6426      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6427      * a cache of Roo.data.Records.
6428      */
6429     readRecords : function(doc){
6430         /**
6431          * After any data loads/reads, the raw XML Document is available for further custom processing.
6432          * @type XMLDocument
6433          */
6434         this.xmlData = doc;
6435         var root = doc.documentElement || doc;
6436         var q = Roo.DomQuery;
6437         var recordType = this.recordType, fields = recordType.prototype.fields;
6438         var sid = this.meta.id;
6439         var totalRecords = 0, success = true;
6440         if(this.meta.totalRecords){
6441             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6442         }
6443         
6444         if(this.meta.success){
6445             var sv = q.selectValue(this.meta.success, root, true);
6446             success = sv !== false && sv !== 'false';
6447         }
6448         var records = [];
6449         var ns = q.select(this.meta.record, root);
6450         for(var i = 0, len = ns.length; i < len; i++) {
6451                 var n = ns[i];
6452                 var values = {};
6453                 var id = sid ? q.selectValue(sid, n) : undefined;
6454                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455                     var f = fields.items[j];
6456                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6457                     v = f.convert(v);
6458                     values[f.name] = v;
6459                 }
6460                 var record = new recordType(values, id);
6461                 record.node = n;
6462                 records[records.length] = record;
6463             }
6464
6465             return {
6466                 success : success,
6467                 records : records,
6468                 totalRecords : totalRecords || records.length
6469             };
6470     }
6471 });/*
6472  * Based on:
6473  * Ext JS Library 1.1.1
6474  * Copyright(c) 2006-2007, Ext JS, LLC.
6475  *
6476  * Originally Released Under LGPL - original licence link has changed is not relivant.
6477  *
6478  * Fork - LGPL
6479  * <script type="text/javascript">
6480  */
6481
6482 /**
6483  * @class Roo.data.ArrayReader
6484  * @extends Roo.data.DataReader
6485  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6486  * Each element of that Array represents a row of data fields. The
6487  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6488  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6489  * <p>
6490  * Example code:.
6491  * <pre><code>
6492 var RecordDef = Roo.data.Record.create([
6493     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6494     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6495 ]);
6496 var myReader = new Roo.data.ArrayReader({
6497     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6498 }, RecordDef);
6499 </code></pre>
6500  * <p>
6501  * This would consume an Array like this:
6502  * <pre><code>
6503 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6504   </code></pre>
6505  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6506  * @constructor
6507  * Create a new JsonReader
6508  * @param {Object} meta Metadata configuration options.
6509  * @param {Object} recordType Either an Array of field definition objects
6510  * as specified to {@link Roo.data.Record#create},
6511  * or an {@link Roo.data.Record} object
6512  * created using {@link Roo.data.Record#create}.
6513  */
6514 Roo.data.ArrayReader = function(meta, recordType){
6515     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6516 };
6517
6518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6519     /**
6520      * Create a data block containing Roo.data.Records from an XML document.
6521      * @param {Object} o An Array of row objects which represents the dataset.
6522      * @return {Object} data A data block which is used by an Roo.data.Store object as
6523      * a cache of Roo.data.Records.
6524      */
6525     readRecords : function(o){
6526         var sid = this.meta ? this.meta.id : null;
6527         var recordType = this.recordType, fields = recordType.prototype.fields;
6528         var records = [];
6529         var root = o;
6530             for(var i = 0; i < root.length; i++){
6531                     var n = root[i];
6532                 var values = {};
6533                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6534                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6535                 var f = fields.items[j];
6536                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6537                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6538                 v = f.convert(v);
6539                 values[f.name] = v;
6540             }
6541                 var record = new recordType(values, id);
6542                 record.json = n;
6543                 records[records.length] = record;
6544             }
6545             return {
6546                 records : records,
6547                 totalRecords : records.length
6548             };
6549     }
6550 });/*
6551  * Based on:
6552  * Ext JS Library 1.1.1
6553  * Copyright(c) 2006-2007, Ext JS, LLC.
6554  *
6555  * Originally Released Under LGPL - original licence link has changed is not relivant.
6556  *
6557  * Fork - LGPL
6558  * <script type="text/javascript">
6559  */
6560
6561
6562 /**
6563  * @class Roo.data.Tree
6564  * @extends Roo.util.Observable
6565  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6566  * in the tree have most standard DOM functionality.
6567  * @constructor
6568  * @param {Node} root (optional) The root node
6569  */
6570 Roo.data.Tree = function(root){
6571    this.nodeHash = {};
6572    /**
6573     * The root node for this tree
6574     * @type Node
6575     */
6576    this.root = null;
6577    if(root){
6578        this.setRootNode(root);
6579    }
6580    this.addEvents({
6581        /**
6582         * @event append
6583         * Fires when a new child node is appended to a node in this tree.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The newly appended node
6587         * @param {Number} index The index of the newly appended node
6588         */
6589        "append" : true,
6590        /**
6591         * @event remove
6592         * Fires when a child node is removed from a node in this tree.
6593         * @param {Tree} tree The owner tree
6594         * @param {Node} parent The parent node
6595         * @param {Node} node The child node removed
6596         */
6597        "remove" : true,
6598        /**
6599         * @event move
6600         * Fires when a node is moved to a new location in the tree
6601         * @param {Tree} tree The owner tree
6602         * @param {Node} node The node moved
6603         * @param {Node} oldParent The old parent of this node
6604         * @param {Node} newParent The new parent of this node
6605         * @param {Number} index The index it was moved to
6606         */
6607        "move" : true,
6608        /**
6609         * @event insert
6610         * Fires when a new child node is inserted in a node in this tree.
6611         * @param {Tree} tree The owner tree
6612         * @param {Node} parent The parent node
6613         * @param {Node} node The child node inserted
6614         * @param {Node} refNode The child node the node was inserted before
6615         */
6616        "insert" : true,
6617        /**
6618         * @event beforeappend
6619         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6620         * @param {Tree} tree The owner tree
6621         * @param {Node} parent The parent node
6622         * @param {Node} node The child node to be appended
6623         */
6624        "beforeappend" : true,
6625        /**
6626         * @event beforeremove
6627         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6628         * @param {Tree} tree The owner tree
6629         * @param {Node} parent The parent node
6630         * @param {Node} node The child node to be removed
6631         */
6632        "beforeremove" : true,
6633        /**
6634         * @event beforemove
6635         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6636         * @param {Tree} tree The owner tree
6637         * @param {Node} node The node being moved
6638         * @param {Node} oldParent The parent of the node
6639         * @param {Node} newParent The new parent the node is moving to
6640         * @param {Number} index The index it is being moved to
6641         */
6642        "beforemove" : true,
6643        /**
6644         * @event beforeinsert
6645         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} parent The parent node
6648         * @param {Node} node The child node to be inserted
6649         * @param {Node} refNode The child node the node is being inserted before
6650         */
6651        "beforeinsert" : true
6652    });
6653
6654     Roo.data.Tree.superclass.constructor.call(this);
6655 };
6656
6657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6658     pathSeparator: "/",
6659
6660     proxyNodeEvent : function(){
6661         return this.fireEvent.apply(this, arguments);
6662     },
6663
6664     /**
6665      * Returns the root node for this tree.
6666      * @return {Node}
6667      */
6668     getRootNode : function(){
6669         return this.root;
6670     },
6671
6672     /**
6673      * Sets the root node for this tree.
6674      * @param {Node} node
6675      * @return {Node}
6676      */
6677     setRootNode : function(node){
6678         this.root = node;
6679         node.ownerTree = this;
6680         node.isRoot = true;
6681         this.registerNode(node);
6682         return node;
6683     },
6684
6685     /**
6686      * Gets a node in this tree by its id.
6687      * @param {String} id
6688      * @return {Node}
6689      */
6690     getNodeById : function(id){
6691         return this.nodeHash[id];
6692     },
6693
6694     registerNode : function(node){
6695         this.nodeHash[node.id] = node;
6696     },
6697
6698     unregisterNode : function(node){
6699         delete this.nodeHash[node.id];
6700     },
6701
6702     toString : function(){
6703         return "[Tree"+(this.id?" "+this.id:"")+"]";
6704     }
6705 });
6706
6707 /**
6708  * @class Roo.data.Node
6709  * @extends Roo.util.Observable
6710  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6711  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6712  * @constructor
6713  * @param {Object} attributes The attributes/config for the node
6714  */
6715 Roo.data.Node = function(attributes){
6716     /**
6717      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6718      * @type {Object}
6719      */
6720     this.attributes = attributes || {};
6721     this.leaf = this.attributes.leaf;
6722     /**
6723      * The node id. @type String
6724      */
6725     this.id = this.attributes.id;
6726     if(!this.id){
6727         this.id = Roo.id(null, "ynode-");
6728         this.attributes.id = this.id;
6729     }
6730      
6731     
6732     /**
6733      * All child nodes of this node. @type Array
6734      */
6735     this.childNodes = [];
6736     if(!this.childNodes.indexOf){ // indexOf is a must
6737         this.childNodes.indexOf = function(o){
6738             for(var i = 0, len = this.length; i < len; i++){
6739                 if(this[i] == o) {
6740                     return i;
6741                 }
6742             }
6743             return -1;
6744         };
6745     }
6746     /**
6747      * The parent node for this node. @type Node
6748      */
6749     this.parentNode = null;
6750     /**
6751      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6752      */
6753     this.firstChild = null;
6754     /**
6755      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6756      */
6757     this.lastChild = null;
6758     /**
6759      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6760      */
6761     this.previousSibling = null;
6762     /**
6763      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6764      */
6765     this.nextSibling = null;
6766
6767     this.addEvents({
6768        /**
6769         * @event append
6770         * Fires when a new child node is appended
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The newly appended node
6774         * @param {Number} index The index of the newly appended node
6775         */
6776        "append" : true,
6777        /**
6778         * @event remove
6779         * Fires when a child node is removed
6780         * @param {Tree} tree The owner tree
6781         * @param {Node} this This node
6782         * @param {Node} node The removed node
6783         */
6784        "remove" : true,
6785        /**
6786         * @event move
6787         * Fires when this node is moved to a new location in the tree
6788         * @param {Tree} tree The owner tree
6789         * @param {Node} this This node
6790         * @param {Node} oldParent The old parent of this node
6791         * @param {Node} newParent The new parent of this node
6792         * @param {Number} index The index it was moved to
6793         */
6794        "move" : true,
6795        /**
6796         * @event insert
6797         * Fires when a new child node is inserted.
6798         * @param {Tree} tree The owner tree
6799         * @param {Node} this This node
6800         * @param {Node} node The child node inserted
6801         * @param {Node} refNode The child node the node was inserted before
6802         */
6803        "insert" : true,
6804        /**
6805         * @event beforeappend
6806         * Fires before a new child is appended, return false to cancel the append.
6807         * @param {Tree} tree The owner tree
6808         * @param {Node} this This node
6809         * @param {Node} node The child node to be appended
6810         */
6811        "beforeappend" : true,
6812        /**
6813         * @event beforeremove
6814         * Fires before a child is removed, return false to cancel the remove.
6815         * @param {Tree} tree The owner tree
6816         * @param {Node} this This node
6817         * @param {Node} node The child node to be removed
6818         */
6819        "beforeremove" : true,
6820        /**
6821         * @event beforemove
6822         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6823         * @param {Tree} tree The owner tree
6824         * @param {Node} this This node
6825         * @param {Node} oldParent The parent of this node
6826         * @param {Node} newParent The new parent this node is moving to
6827         * @param {Number} index The index it is being moved to
6828         */
6829        "beforemove" : true,
6830        /**
6831         * @event beforeinsert
6832         * Fires before a new child is inserted, return false to cancel the insert.
6833         * @param {Tree} tree The owner tree
6834         * @param {Node} this This node
6835         * @param {Node} node The child node to be inserted
6836         * @param {Node} refNode The child node the node is being inserted before
6837         */
6838        "beforeinsert" : true
6839    });
6840     this.listeners = this.attributes.listeners;
6841     Roo.data.Node.superclass.constructor.call(this);
6842 };
6843
6844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6845     fireEvent : function(evtName){
6846         // first do standard event for this node
6847         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6848             return false;
6849         }
6850         // then bubble it up to the tree if the event wasn't cancelled
6851         var ot = this.getOwnerTree();
6852         if(ot){
6853             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6854                 return false;
6855             }
6856         }
6857         return true;
6858     },
6859
6860     /**
6861      * Returns true if this node is a leaf
6862      * @return {Boolean}
6863      */
6864     isLeaf : function(){
6865         return this.leaf === true;
6866     },
6867
6868     // private
6869     setFirstChild : function(node){
6870         this.firstChild = node;
6871     },
6872
6873     //private
6874     setLastChild : function(node){
6875         this.lastChild = node;
6876     },
6877
6878
6879     /**
6880      * Returns true if this node is the last child of its parent
6881      * @return {Boolean}
6882      */
6883     isLast : function(){
6884        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6885     },
6886
6887     /**
6888      * Returns true if this node is the first child of its parent
6889      * @return {Boolean}
6890      */
6891     isFirst : function(){
6892        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6893     },
6894
6895     hasChildNodes : function(){
6896         return !this.isLeaf() && this.childNodes.length > 0;
6897     },
6898
6899     /**
6900      * Insert node(s) as the last child node of this node.
6901      * @param {Node/Array} node The node or Array of nodes to append
6902      * @return {Node} The appended node if single append, or null if an array was passed
6903      */
6904     appendChild : function(node){
6905         var multi = false;
6906         if(node instanceof Array){
6907             multi = node;
6908         }else if(arguments.length > 1){
6909             multi = arguments;
6910         }
6911         // if passed an array or multiple args do them one by one
6912         if(multi){
6913             for(var i = 0, len = multi.length; i < len; i++) {
6914                 this.appendChild(multi[i]);
6915             }
6916         }else{
6917             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6918                 return false;
6919             }
6920             var index = this.childNodes.length;
6921             var oldParent = node.parentNode;
6922             // it's a move, make sure we move it cleanly
6923             if(oldParent){
6924                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6925                     return false;
6926                 }
6927                 oldParent.removeChild(node);
6928             }
6929             index = this.childNodes.length;
6930             if(index == 0){
6931                 this.setFirstChild(node);
6932             }
6933             this.childNodes.push(node);
6934             node.parentNode = this;
6935             var ps = this.childNodes[index-1];
6936             if(ps){
6937                 node.previousSibling = ps;
6938                 ps.nextSibling = node;
6939             }else{
6940                 node.previousSibling = null;
6941             }
6942             node.nextSibling = null;
6943             this.setLastChild(node);
6944             node.setOwnerTree(this.getOwnerTree());
6945             this.fireEvent("append", this.ownerTree, this, node, index);
6946             if(oldParent){
6947                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6948             }
6949             return node;
6950         }
6951     },
6952
6953     /**
6954      * Removes a child node from this node.
6955      * @param {Node} node The node to remove
6956      * @return {Node} The removed node
6957      */
6958     removeChild : function(node){
6959         var index = this.childNodes.indexOf(node);
6960         if(index == -1){
6961             return false;
6962         }
6963         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6964             return false;
6965         }
6966
6967         // remove it from childNodes collection
6968         this.childNodes.splice(index, 1);
6969
6970         // update siblings
6971         if(node.previousSibling){
6972             node.previousSibling.nextSibling = node.nextSibling;
6973         }
6974         if(node.nextSibling){
6975             node.nextSibling.previousSibling = node.previousSibling;
6976         }
6977
6978         // update child refs
6979         if(this.firstChild == node){
6980             this.setFirstChild(node.nextSibling);
6981         }
6982         if(this.lastChild == node){
6983             this.setLastChild(node.previousSibling);
6984         }
6985
6986         node.setOwnerTree(null);
6987         // clear any references from the node
6988         node.parentNode = null;
6989         node.previousSibling = null;
6990         node.nextSibling = null;
6991         this.fireEvent("remove", this.ownerTree, this, node);
6992         return node;
6993     },
6994
6995     /**
6996      * Inserts the first node before the second node in this nodes childNodes collection.
6997      * @param {Node} node The node to insert
6998      * @param {Node} refNode The node to insert before (if null the node is appended)
6999      * @return {Node} The inserted node
7000      */
7001     insertBefore : function(node, refNode){
7002         if(!refNode){ // like standard Dom, refNode can be null for append
7003             return this.appendChild(node);
7004         }
7005         // nothing to do
7006         if(node == refNode){
7007             return false;
7008         }
7009
7010         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7011             return false;
7012         }
7013         var index = this.childNodes.indexOf(refNode);
7014         var oldParent = node.parentNode;
7015         var refIndex = index;
7016
7017         // when moving internally, indexes will change after remove
7018         if(oldParent == this && this.childNodes.indexOf(node) < index){
7019             refIndex--;
7020         }
7021
7022         // it's a move, make sure we move it cleanly
7023         if(oldParent){
7024             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7025                 return false;
7026             }
7027             oldParent.removeChild(node);
7028         }
7029         if(refIndex == 0){
7030             this.setFirstChild(node);
7031         }
7032         this.childNodes.splice(refIndex, 0, node);
7033         node.parentNode = this;
7034         var ps = this.childNodes[refIndex-1];
7035         if(ps){
7036             node.previousSibling = ps;
7037             ps.nextSibling = node;
7038         }else{
7039             node.previousSibling = null;
7040         }
7041         node.nextSibling = refNode;
7042         refNode.previousSibling = node;
7043         node.setOwnerTree(this.getOwnerTree());
7044         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7045         if(oldParent){
7046             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7047         }
7048         return node;
7049     },
7050
7051     /**
7052      * Returns the child node at the specified index.
7053      * @param {Number} index
7054      * @return {Node}
7055      */
7056     item : function(index){
7057         return this.childNodes[index];
7058     },
7059
7060     /**
7061      * Replaces one child node in this node with another.
7062      * @param {Node} newChild The replacement node
7063      * @param {Node} oldChild The node to replace
7064      * @return {Node} The replaced node
7065      */
7066     replaceChild : function(newChild, oldChild){
7067         this.insertBefore(newChild, oldChild);
7068         this.removeChild(oldChild);
7069         return oldChild;
7070     },
7071
7072     /**
7073      * Returns the index of a child node
7074      * @param {Node} node
7075      * @return {Number} The index of the node or -1 if it was not found
7076      */
7077     indexOf : function(child){
7078         return this.childNodes.indexOf(child);
7079     },
7080
7081     /**
7082      * Returns the tree this node is in.
7083      * @return {Tree}
7084      */
7085     getOwnerTree : function(){
7086         // if it doesn't have one, look for one
7087         if(!this.ownerTree){
7088             var p = this;
7089             while(p){
7090                 if(p.ownerTree){
7091                     this.ownerTree = p.ownerTree;
7092                     break;
7093                 }
7094                 p = p.parentNode;
7095             }
7096         }
7097         return this.ownerTree;
7098     },
7099
7100     /**
7101      * Returns depth of this node (the root node has a depth of 0)
7102      * @return {Number}
7103      */
7104     getDepth : function(){
7105         var depth = 0;
7106         var p = this;
7107         while(p.parentNode){
7108             ++depth;
7109             p = p.parentNode;
7110         }
7111         return depth;
7112     },
7113
7114     // private
7115     setOwnerTree : function(tree){
7116         // if it's move, we need to update everyone
7117         if(tree != this.ownerTree){
7118             if(this.ownerTree){
7119                 this.ownerTree.unregisterNode(this);
7120             }
7121             this.ownerTree = tree;
7122             var cs = this.childNodes;
7123             for(var i = 0, len = cs.length; i < len; i++) {
7124                 cs[i].setOwnerTree(tree);
7125             }
7126             if(tree){
7127                 tree.registerNode(this);
7128             }
7129         }
7130     },
7131
7132     /**
7133      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7134      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7135      * @return {String} The path
7136      */
7137     getPath : function(attr){
7138         attr = attr || "id";
7139         var p = this.parentNode;
7140         var b = [this.attributes[attr]];
7141         while(p){
7142             b.unshift(p.attributes[attr]);
7143             p = p.parentNode;
7144         }
7145         var sep = this.getOwnerTree().pathSeparator;
7146         return sep + b.join(sep);
7147     },
7148
7149     /**
7150      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7151      * function call will be the scope provided or the current node. The arguments to the function
7152      * will be the args provided or the current node. If the function returns false at any point,
7153      * the bubble is stopped.
7154      * @param {Function} fn The function to call
7155      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7156      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7157      */
7158     bubble : function(fn, scope, args){
7159         var p = this;
7160         while(p){
7161             if(fn.call(scope || p, args || p) === false){
7162                 break;
7163             }
7164             p = p.parentNode;
7165         }
7166     },
7167
7168     /**
7169      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7170      * function call will be the scope provided or the current node. The arguments to the function
7171      * will be the args provided or the current node. If the function returns false at any point,
7172      * the cascade is stopped on that branch.
7173      * @param {Function} fn The function to call
7174      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7175      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7176      */
7177     cascade : function(fn, scope, args){
7178         if(fn.call(scope || this, args || this) !== false){
7179             var cs = this.childNodes;
7180             for(var i = 0, len = cs.length; i < len; i++) {
7181                 cs[i].cascade(fn, scope, args);
7182             }
7183         }
7184     },
7185
7186     /**
7187      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7188      * function call will be the scope provided or the current node. The arguments to the function
7189      * will be the args provided or the current node. If the function returns false at any point,
7190      * the iteration stops.
7191      * @param {Function} fn The function to call
7192      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7193      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7194      */
7195     eachChild : function(fn, scope, args){
7196         var cs = this.childNodes;
7197         for(var i = 0, len = cs.length; i < len; i++) {
7198                 if(fn.call(scope || this, args || cs[i]) === false){
7199                     break;
7200                 }
7201         }
7202     },
7203
7204     /**
7205      * Finds the first child that has the attribute with the specified value.
7206      * @param {String} attribute The attribute name
7207      * @param {Mixed} value The value to search for
7208      * @return {Node} The found child or null if none was found
7209      */
7210     findChild : function(attribute, value){
7211         var cs = this.childNodes;
7212         for(var i = 0, len = cs.length; i < len; i++) {
7213                 if(cs[i].attributes[attribute] == value){
7214                     return cs[i];
7215                 }
7216         }
7217         return null;
7218     },
7219
7220     /**
7221      * Finds the first child by a custom function. The child matches if the function passed
7222      * returns true.
7223      * @param {Function} fn
7224      * @param {Object} scope (optional)
7225      * @return {Node} The found child or null if none was found
7226      */
7227     findChildBy : function(fn, scope){
7228         var cs = this.childNodes;
7229         for(var i = 0, len = cs.length; i < len; i++) {
7230                 if(fn.call(scope||cs[i], cs[i]) === true){
7231                     return cs[i];
7232                 }
7233         }
7234         return null;
7235     },
7236
7237     /**
7238      * Sorts this nodes children using the supplied sort function
7239      * @param {Function} fn
7240      * @param {Object} scope (optional)
7241      */
7242     sort : function(fn, scope){
7243         var cs = this.childNodes;
7244         var len = cs.length;
7245         if(len > 0){
7246             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7247             cs.sort(sortFn);
7248             for(var i = 0; i < len; i++){
7249                 var n = cs[i];
7250                 n.previousSibling = cs[i-1];
7251                 n.nextSibling = cs[i+1];
7252                 if(i == 0){
7253                     this.setFirstChild(n);
7254                 }
7255                 if(i == len-1){
7256                     this.setLastChild(n);
7257                 }
7258             }
7259         }
7260     },
7261
7262     /**
7263      * Returns true if this node is an ancestor (at any point) of the passed node.
7264      * @param {Node} node
7265      * @return {Boolean}
7266      */
7267     contains : function(node){
7268         return node.isAncestor(this);
7269     },
7270
7271     /**
7272      * Returns true if the passed node is an ancestor (at any point) of this node.
7273      * @param {Node} node
7274      * @return {Boolean}
7275      */
7276     isAncestor : function(node){
7277         var p = this.parentNode;
7278         while(p){
7279             if(p == node){
7280                 return true;
7281             }
7282             p = p.parentNode;
7283         }
7284         return false;
7285     },
7286
7287     toString : function(){
7288         return "[Node"+(this.id?" "+this.id:"")+"]";
7289     }
7290 });/*
7291  * Based on:
7292  * Ext JS Library 1.1.1
7293  * Copyright(c) 2006-2007, Ext JS, LLC.
7294  *
7295  * Originally Released Under LGPL - original licence link has changed is not relivant.
7296  *
7297  * Fork - LGPL
7298  * <script type="text/javascript">
7299  */
7300  (function(){ 
7301 /**
7302  * @class Roo.Layer
7303  * @extends Roo.Element
7304  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7305  * automatic maintaining of shadow/shim positions.
7306  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7307  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7308  * you can pass a string with a CSS class name. False turns off the shadow.
7309  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7310  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7311  * @cfg {String} cls CSS class to add to the element
7312  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7313  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7314  * @constructor
7315  * @param {Object} config An object with config options.
7316  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7317  */
7318
7319 Roo.Layer = function(config, existingEl){
7320     config = config || {};
7321     var dh = Roo.DomHelper;
7322     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7323     if(existingEl){
7324         this.dom = Roo.getDom(existingEl);
7325     }
7326     if(!this.dom){
7327         var o = config.dh || {tag: "div", cls: "x-layer"};
7328         this.dom = dh.append(pel, o);
7329     }
7330     if(config.cls){
7331         this.addClass(config.cls);
7332     }
7333     this.constrain = config.constrain !== false;
7334     this.visibilityMode = Roo.Element.VISIBILITY;
7335     if(config.id){
7336         this.id = this.dom.id = config.id;
7337     }else{
7338         this.id = Roo.id(this.dom);
7339     }
7340     this.zindex = config.zindex || this.getZIndex();
7341     this.position("absolute", this.zindex);
7342     if(config.shadow){
7343         this.shadowOffset = config.shadowOffset || 4;
7344         this.shadow = new Roo.Shadow({
7345             offset : this.shadowOffset,
7346             mode : config.shadow
7347         });
7348     }else{
7349         this.shadowOffset = 0;
7350     }
7351     this.useShim = config.shim !== false && Roo.useShims;
7352     this.useDisplay = config.useDisplay;
7353     this.hide();
7354 };
7355
7356 var supr = Roo.Element.prototype;
7357
7358 // shims are shared among layer to keep from having 100 iframes
7359 var shims = [];
7360
7361 Roo.extend(Roo.Layer, Roo.Element, {
7362
7363     getZIndex : function(){
7364         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7365     },
7366
7367     getShim : function(){
7368         if(!this.useShim){
7369             return null;
7370         }
7371         if(this.shim){
7372             return this.shim;
7373         }
7374         var shim = shims.shift();
7375         if(!shim){
7376             shim = this.createShim();
7377             shim.enableDisplayMode('block');
7378             shim.dom.style.display = 'none';
7379             shim.dom.style.visibility = 'visible';
7380         }
7381         var pn = this.dom.parentNode;
7382         if(shim.dom.parentNode != pn){
7383             pn.insertBefore(shim.dom, this.dom);
7384         }
7385         shim.setStyle('z-index', this.getZIndex()-2);
7386         this.shim = shim;
7387         return shim;
7388     },
7389
7390     hideShim : function(){
7391         if(this.shim){
7392             this.shim.setDisplayed(false);
7393             shims.push(this.shim);
7394             delete this.shim;
7395         }
7396     },
7397
7398     disableShadow : function(){
7399         if(this.shadow){
7400             this.shadowDisabled = true;
7401             this.shadow.hide();
7402             this.lastShadowOffset = this.shadowOffset;
7403             this.shadowOffset = 0;
7404         }
7405     },
7406
7407     enableShadow : function(show){
7408         if(this.shadow){
7409             this.shadowDisabled = false;
7410             this.shadowOffset = this.lastShadowOffset;
7411             delete this.lastShadowOffset;
7412             if(show){
7413                 this.sync(true);
7414             }
7415         }
7416     },
7417
7418     // private
7419     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7420     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7421     sync : function(doShow){
7422         var sw = this.shadow;
7423         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7424             var sh = this.getShim();
7425
7426             var w = this.getWidth(),
7427                 h = this.getHeight();
7428
7429             var l = this.getLeft(true),
7430                 t = this.getTop(true);
7431
7432             if(sw && !this.shadowDisabled){
7433                 if(doShow && !sw.isVisible()){
7434                     sw.show(this);
7435                 }else{
7436                     sw.realign(l, t, w, h);
7437                 }
7438                 if(sh){
7439                     if(doShow){
7440                        sh.show();
7441                     }
7442                     // fit the shim behind the shadow, so it is shimmed too
7443                     var a = sw.adjusts, s = sh.dom.style;
7444                     s.left = (Math.min(l, l+a.l))+"px";
7445                     s.top = (Math.min(t, t+a.t))+"px";
7446                     s.width = (w+a.w)+"px";
7447                     s.height = (h+a.h)+"px";
7448                 }
7449             }else if(sh){
7450                 if(doShow){
7451                    sh.show();
7452                 }
7453                 sh.setSize(w, h);
7454                 sh.setLeftTop(l, t);
7455             }
7456             
7457         }
7458     },
7459
7460     // private
7461     destroy : function(){
7462         this.hideShim();
7463         if(this.shadow){
7464             this.shadow.hide();
7465         }
7466         this.removeAllListeners();
7467         var pn = this.dom.parentNode;
7468         if(pn){
7469             pn.removeChild(this.dom);
7470         }
7471         Roo.Element.uncache(this.id);
7472     },
7473
7474     remove : function(){
7475         this.destroy();
7476     },
7477
7478     // private
7479     beginUpdate : function(){
7480         this.updating = true;
7481     },
7482
7483     // private
7484     endUpdate : function(){
7485         this.updating = false;
7486         this.sync(true);
7487     },
7488
7489     // private
7490     hideUnders : function(negOffset){
7491         if(this.shadow){
7492             this.shadow.hide();
7493         }
7494         this.hideShim();
7495     },
7496
7497     // private
7498     constrainXY : function(){
7499         if(this.constrain){
7500             var vw = Roo.lib.Dom.getViewWidth(),
7501                 vh = Roo.lib.Dom.getViewHeight();
7502             var s = Roo.get(document).getScroll();
7503
7504             var xy = this.getXY();
7505             var x = xy[0], y = xy[1];   
7506             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7507             // only move it if it needs it
7508             var moved = false;
7509             // first validate right/bottom
7510             if((x + w) > vw+s.left){
7511                 x = vw - w - this.shadowOffset;
7512                 moved = true;
7513             }
7514             if((y + h) > vh+s.top){
7515                 y = vh - h - this.shadowOffset;
7516                 moved = true;
7517             }
7518             // then make sure top/left isn't negative
7519             if(x < s.left){
7520                 x = s.left;
7521                 moved = true;
7522             }
7523             if(y < s.top){
7524                 y = s.top;
7525                 moved = true;
7526             }
7527             if(moved){
7528                 if(this.avoidY){
7529                     var ay = this.avoidY;
7530                     if(y <= ay && (y+h) >= ay){
7531                         y = ay-h-5;   
7532                     }
7533                 }
7534                 xy = [x, y];
7535                 this.storeXY(xy);
7536                 supr.setXY.call(this, xy);
7537                 this.sync();
7538             }
7539         }
7540     },
7541
7542     isVisible : function(){
7543         return this.visible;    
7544     },
7545
7546     // private
7547     showAction : function(){
7548         this.visible = true; // track visibility to prevent getStyle calls
7549         if(this.useDisplay === true){
7550             this.setDisplayed("");
7551         }else if(this.lastXY){
7552             supr.setXY.call(this, this.lastXY);
7553         }else if(this.lastLT){
7554             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7555         }
7556     },
7557
7558     // private
7559     hideAction : function(){
7560         this.visible = false;
7561         if(this.useDisplay === true){
7562             this.setDisplayed(false);
7563         }else{
7564             this.setLeftTop(-10000,-10000);
7565         }
7566     },
7567
7568     // overridden Element method
7569     setVisible : function(v, a, d, c, e){
7570         if(v){
7571             this.showAction();
7572         }
7573         if(a && v){
7574             var cb = function(){
7575                 this.sync(true);
7576                 if(c){
7577                     c();
7578                 }
7579             }.createDelegate(this);
7580             supr.setVisible.call(this, true, true, d, cb, e);
7581         }else{
7582             if(!v){
7583                 this.hideUnders(true);
7584             }
7585             var cb = c;
7586             if(a){
7587                 cb = function(){
7588                     this.hideAction();
7589                     if(c){
7590                         c();
7591                     }
7592                 }.createDelegate(this);
7593             }
7594             supr.setVisible.call(this, v, a, d, cb, e);
7595             if(v){
7596                 this.sync(true);
7597             }else if(!a){
7598                 this.hideAction();
7599             }
7600         }
7601     },
7602
7603     storeXY : function(xy){
7604         delete this.lastLT;
7605         this.lastXY = xy;
7606     },
7607
7608     storeLeftTop : function(left, top){
7609         delete this.lastXY;
7610         this.lastLT = [left, top];
7611     },
7612
7613     // private
7614     beforeFx : function(){
7615         this.beforeAction();
7616         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7617     },
7618
7619     // private
7620     afterFx : function(){
7621         Roo.Layer.superclass.afterFx.apply(this, arguments);
7622         this.sync(this.isVisible());
7623     },
7624
7625     // private
7626     beforeAction : function(){
7627         if(!this.updating && this.shadow){
7628             this.shadow.hide();
7629         }
7630     },
7631
7632     // overridden Element method
7633     setLeft : function(left){
7634         this.storeLeftTop(left, this.getTop(true));
7635         supr.setLeft.apply(this, arguments);
7636         this.sync();
7637     },
7638
7639     setTop : function(top){
7640         this.storeLeftTop(this.getLeft(true), top);
7641         supr.setTop.apply(this, arguments);
7642         this.sync();
7643     },
7644
7645     setLeftTop : function(left, top){
7646         this.storeLeftTop(left, top);
7647         supr.setLeftTop.apply(this, arguments);
7648         this.sync();
7649     },
7650
7651     setXY : function(xy, a, d, c, e){
7652         this.fixDisplay();
7653         this.beforeAction();
7654         this.storeXY(xy);
7655         var cb = this.createCB(c);
7656         supr.setXY.call(this, xy, a, d, cb, e);
7657         if(!a){
7658             cb();
7659         }
7660     },
7661
7662     // private
7663     createCB : function(c){
7664         var el = this;
7665         return function(){
7666             el.constrainXY();
7667             el.sync(true);
7668             if(c){
7669                 c();
7670             }
7671         };
7672     },
7673
7674     // overridden Element method
7675     setX : function(x, a, d, c, e){
7676         this.setXY([x, this.getY()], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setY : function(y, a, d, c, e){
7681         this.setXY([this.getX(), y], a, d, c, e);
7682     },
7683
7684     // overridden Element method
7685     setSize : function(w, h, a, d, c, e){
7686         this.beforeAction();
7687         var cb = this.createCB(c);
7688         supr.setSize.call(this, w, h, a, d, cb, e);
7689         if(!a){
7690             cb();
7691         }
7692     },
7693
7694     // overridden Element method
7695     setWidth : function(w, a, d, c, e){
7696         this.beforeAction();
7697         var cb = this.createCB(c);
7698         supr.setWidth.call(this, w, a, d, cb, e);
7699         if(!a){
7700             cb();
7701         }
7702     },
7703
7704     // overridden Element method
7705     setHeight : function(h, a, d, c, e){
7706         this.beforeAction();
7707         var cb = this.createCB(c);
7708         supr.setHeight.call(this, h, a, d, cb, e);
7709         if(!a){
7710             cb();
7711         }
7712     },
7713
7714     // overridden Element method
7715     setBounds : function(x, y, w, h, a, d, c, e){
7716         this.beforeAction();
7717         var cb = this.createCB(c);
7718         if(!a){
7719             this.storeXY([x, y]);
7720             supr.setXY.call(this, [x, y]);
7721             supr.setSize.call(this, w, h, a, d, cb, e);
7722             cb();
7723         }else{
7724             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7725         }
7726         return this;
7727     },
7728     
7729     /**
7730      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7731      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7732      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7733      * @param {Number} zindex The new z-index to set
7734      * @return {this} The Layer
7735      */
7736     setZIndex : function(zindex){
7737         this.zindex = zindex;
7738         this.setStyle("z-index", zindex + 2);
7739         if(this.shadow){
7740             this.shadow.setZIndex(zindex + 1);
7741         }
7742         if(this.shim){
7743             this.shim.setStyle("z-index", zindex);
7744         }
7745     }
7746 });
7747 })();/*
7748  * Based on:
7749  * Ext JS Library 1.1.1
7750  * Copyright(c) 2006-2007, Ext JS, LLC.
7751  *
7752  * Originally Released Under LGPL - original licence link has changed is not relivant.
7753  *
7754  * Fork - LGPL
7755  * <script type="text/javascript">
7756  */
7757
7758
7759 /**
7760  * @class Roo.Shadow
7761  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7762  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7763  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7764  * @constructor
7765  * Create a new Shadow
7766  * @param {Object} config The config object
7767  */
7768 Roo.Shadow = function(config){
7769     Roo.apply(this, config);
7770     if(typeof this.mode != "string"){
7771         this.mode = this.defaultMode;
7772     }
7773     var o = this.offset, a = {h: 0};
7774     var rad = Math.floor(this.offset/2);
7775     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7776         case "drop":
7777             a.w = 0;
7778             a.l = a.t = o;
7779             a.t -= 1;
7780             if(Roo.isIE){
7781                 a.l -= this.offset + rad;
7782                 a.t -= this.offset + rad;
7783                 a.w -= rad;
7784                 a.h -= rad;
7785                 a.t += 1;
7786             }
7787         break;
7788         case "sides":
7789             a.w = (o*2);
7790             a.l = -o;
7791             a.t = o-1;
7792             if(Roo.isIE){
7793                 a.l -= (this.offset - rad);
7794                 a.t -= this.offset + rad;
7795                 a.l += 1;
7796                 a.w -= (this.offset - rad)*2;
7797                 a.w -= rad + 1;
7798                 a.h -= 1;
7799             }
7800         break;
7801         case "frame":
7802             a.w = a.h = (o*2);
7803             a.l = a.t = -o;
7804             a.t += 1;
7805             a.h -= 2;
7806             if(Roo.isIE){
7807                 a.l -= (this.offset - rad);
7808                 a.t -= (this.offset - rad);
7809                 a.l += 1;
7810                 a.w -= (this.offset + rad + 1);
7811                 a.h -= (this.offset + rad);
7812                 a.h += 1;
7813             }
7814         break;
7815     };
7816
7817     this.adjusts = a;
7818 };
7819
7820 Roo.Shadow.prototype = {
7821     /**
7822      * @cfg {String} mode
7823      * The shadow display mode.  Supports the following options:<br />
7824      * sides: Shadow displays on both sides and bottom only<br />
7825      * frame: Shadow displays equally on all four sides<br />
7826      * drop: Traditional bottom-right drop shadow (default)
7827      */
7828     /**
7829      * @cfg {String} offset
7830      * The number of pixels to offset the shadow from the element (defaults to 4)
7831      */
7832     offset: 4,
7833
7834     // private
7835     defaultMode: "drop",
7836
7837     /**
7838      * Displays the shadow under the target element
7839      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7840      */
7841     show : function(target){
7842         target = Roo.get(target);
7843         if(!this.el){
7844             this.el = Roo.Shadow.Pool.pull();
7845             if(this.el.dom.nextSibling != target.dom){
7846                 this.el.insertBefore(target);
7847             }
7848         }
7849         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7850         if(Roo.isIE){
7851             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7852         }
7853         this.realign(
7854             target.getLeft(true),
7855             target.getTop(true),
7856             target.getWidth(),
7857             target.getHeight()
7858         );
7859         this.el.dom.style.display = "block";
7860     },
7861
7862     /**
7863      * Returns true if the shadow is visible, else false
7864      */
7865     isVisible : function(){
7866         return this.el ? true : false;  
7867     },
7868
7869     /**
7870      * Direct alignment when values are already available. Show must be called at least once before
7871      * calling this method to ensure it is initialized.
7872      * @param {Number} left The target element left position
7873      * @param {Number} top The target element top position
7874      * @param {Number} width The target element width
7875      * @param {Number} height The target element height
7876      */
7877     realign : function(l, t, w, h){
7878         if(!this.el){
7879             return;
7880         }
7881         var a = this.adjusts, d = this.el.dom, s = d.style;
7882         var iea = 0;
7883         s.left = (l+a.l)+"px";
7884         s.top = (t+a.t)+"px";
7885         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7886  
7887         if(s.width != sws || s.height != shs){
7888             s.width = sws;
7889             s.height = shs;
7890             if(!Roo.isIE){
7891                 var cn = d.childNodes;
7892                 var sww = Math.max(0, (sw-12))+"px";
7893                 cn[0].childNodes[1].style.width = sww;
7894                 cn[1].childNodes[1].style.width = sww;
7895                 cn[2].childNodes[1].style.width = sww;
7896                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7897             }
7898         }
7899     },
7900
7901     /**
7902      * Hides this shadow
7903      */
7904     hide : function(){
7905         if(this.el){
7906             this.el.dom.style.display = "none";
7907             Roo.Shadow.Pool.push(this.el);
7908             delete this.el;
7909         }
7910     },
7911
7912     /**
7913      * Adjust the z-index of this shadow
7914      * @param {Number} zindex The new z-index
7915      */
7916     setZIndex : function(z){
7917         this.zIndex = z;
7918         if(this.el){
7919             this.el.setStyle("z-index", z);
7920         }
7921     }
7922 };
7923
7924 // Private utility class that manages the internal Shadow cache
7925 Roo.Shadow.Pool = function(){
7926     var p = [];
7927     var markup = Roo.isIE ?
7928                  '<div class="x-ie-shadow"></div>' :
7929                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
7930     return {
7931         pull : function(){
7932             var sh = p.shift();
7933             if(!sh){
7934                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7935                 sh.autoBoxAdjust = false;
7936             }
7937             return sh;
7938         },
7939
7940         push : function(sh){
7941             p.push(sh);
7942         }
7943     };
7944 }();/*
7945  * Based on:
7946  * Ext JS Library 1.1.1
7947  * Copyright(c) 2006-2007, Ext JS, LLC.
7948  *
7949  * Originally Released Under LGPL - original licence link has changed is not relivant.
7950  *
7951  * Fork - LGPL
7952  * <script type="text/javascript">
7953  */
7954
7955
7956 /**
7957  * @class Roo.SplitBar
7958  * @extends Roo.util.Observable
7959  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7960  * <br><br>
7961  * Usage:
7962  * <pre><code>
7963 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7964                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7965 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7966 split.minSize = 100;
7967 split.maxSize = 600;
7968 split.animate = true;
7969 split.on('moved', splitterMoved);
7970 </code></pre>
7971  * @constructor
7972  * Create a new SplitBar
7973  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7974  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7975  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7976  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7977                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7978                         position of the SplitBar).
7979  */
7980 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7981     
7982     /** @private */
7983     this.el = Roo.get(dragElement, true);
7984     this.el.dom.unselectable = "on";
7985     /** @private */
7986     this.resizingEl = Roo.get(resizingElement, true);
7987
7988     /**
7989      * @private
7990      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7991      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7992      * @type Number
7993      */
7994     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7995     
7996     /**
7997      * The minimum size of the resizing element. (Defaults to 0)
7998      * @type Number
7999      */
8000     this.minSize = 0;
8001     
8002     /**
8003      * The maximum size of the resizing element. (Defaults to 2000)
8004      * @type Number
8005      */
8006     this.maxSize = 2000;
8007     
8008     /**
8009      * Whether to animate the transition to the new size
8010      * @type Boolean
8011      */
8012     this.animate = false;
8013     
8014     /**
8015      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8016      * @type Boolean
8017      */
8018     this.useShim = false;
8019     
8020     /** @private */
8021     this.shim = null;
8022     
8023     if(!existingProxy){
8024         /** @private */
8025         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8026     }else{
8027         this.proxy = Roo.get(existingProxy).dom;
8028     }
8029     /** @private */
8030     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8031     
8032     /** @private */
8033     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8034     
8035     /** @private */
8036     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8037     
8038     /** @private */
8039     this.dragSpecs = {};
8040     
8041     /**
8042      * @private The adapter to use to positon and resize elements
8043      */
8044     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8045     this.adapter.init(this);
8046     
8047     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8048         /** @private */
8049         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8050         this.el.addClass("x-splitbar-h");
8051     }else{
8052         /** @private */
8053         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8054         this.el.addClass("x-splitbar-v");
8055     }
8056     
8057     this.addEvents({
8058         /**
8059          * @event resize
8060          * Fires when the splitter is moved (alias for {@link #event-moved})
8061          * @param {Roo.SplitBar} this
8062          * @param {Number} newSize the new width or height
8063          */
8064         "resize" : true,
8065         /**
8066          * @event moved
8067          * Fires when the splitter is moved
8068          * @param {Roo.SplitBar} this
8069          * @param {Number} newSize the new width or height
8070          */
8071         "moved" : true,
8072         /**
8073          * @event beforeresize
8074          * Fires before the splitter is dragged
8075          * @param {Roo.SplitBar} this
8076          */
8077         "beforeresize" : true,
8078
8079         "beforeapply" : true
8080     });
8081
8082     Roo.util.Observable.call(this);
8083 };
8084
8085 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8086     onStartProxyDrag : function(x, y){
8087         this.fireEvent("beforeresize", this);
8088         if(!this.overlay){
8089             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8090             o.unselectable();
8091             o.enableDisplayMode("block");
8092             // all splitbars share the same overlay
8093             Roo.SplitBar.prototype.overlay = o;
8094         }
8095         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8096         this.overlay.show();
8097         Roo.get(this.proxy).setDisplayed("block");
8098         var size = this.adapter.getElementSize(this);
8099         this.activeMinSize = this.getMinimumSize();;
8100         this.activeMaxSize = this.getMaximumSize();;
8101         var c1 = size - this.activeMinSize;
8102         var c2 = Math.max(this.activeMaxSize - size, 0);
8103         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8104             this.dd.resetConstraints();
8105             this.dd.setXConstraint(
8106                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8107                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8108             );
8109             this.dd.setYConstraint(0, 0);
8110         }else{
8111             this.dd.resetConstraints();
8112             this.dd.setXConstraint(0, 0);
8113             this.dd.setYConstraint(
8114                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8115                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8116             );
8117          }
8118         this.dragSpecs.startSize = size;
8119         this.dragSpecs.startPoint = [x, y];
8120         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8121     },
8122     
8123     /** 
8124      * @private Called after the drag operation by the DDProxy
8125      */
8126     onEndProxyDrag : function(e){
8127         Roo.get(this.proxy).setDisplayed(false);
8128         var endPoint = Roo.lib.Event.getXY(e);
8129         if(this.overlay){
8130             this.overlay.hide();
8131         }
8132         var newSize;
8133         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8134             newSize = this.dragSpecs.startSize + 
8135                 (this.placement == Roo.SplitBar.LEFT ?
8136                     endPoint[0] - this.dragSpecs.startPoint[0] :
8137                     this.dragSpecs.startPoint[0] - endPoint[0]
8138                 );
8139         }else{
8140             newSize = this.dragSpecs.startSize + 
8141                 (this.placement == Roo.SplitBar.TOP ?
8142                     endPoint[1] - this.dragSpecs.startPoint[1] :
8143                     this.dragSpecs.startPoint[1] - endPoint[1]
8144                 );
8145         }
8146         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8147         if(newSize != this.dragSpecs.startSize){
8148             if(this.fireEvent('beforeapply', this, newSize) !== false){
8149                 this.adapter.setElementSize(this, newSize);
8150                 this.fireEvent("moved", this, newSize);
8151                 this.fireEvent("resize", this, newSize);
8152             }
8153         }
8154     },
8155     
8156     /**
8157      * Get the adapter this SplitBar uses
8158      * @return The adapter object
8159      */
8160     getAdapter : function(){
8161         return this.adapter;
8162     },
8163     
8164     /**
8165      * Set the adapter this SplitBar uses
8166      * @param {Object} adapter A SplitBar adapter object
8167      */
8168     setAdapter : function(adapter){
8169         this.adapter = adapter;
8170         this.adapter.init(this);
8171     },
8172     
8173     /**
8174      * Gets the minimum size for the resizing element
8175      * @return {Number} The minimum size
8176      */
8177     getMinimumSize : function(){
8178         return this.minSize;
8179     },
8180     
8181     /**
8182      * Sets the minimum size for the resizing element
8183      * @param {Number} minSize The minimum size
8184      */
8185     setMinimumSize : function(minSize){
8186         this.minSize = minSize;
8187     },
8188     
8189     /**
8190      * Gets the maximum size for the resizing element
8191      * @return {Number} The maximum size
8192      */
8193     getMaximumSize : function(){
8194         return this.maxSize;
8195     },
8196     
8197     /**
8198      * Sets the maximum size for the resizing element
8199      * @param {Number} maxSize The maximum size
8200      */
8201     setMaximumSize : function(maxSize){
8202         this.maxSize = maxSize;
8203     },
8204     
8205     /**
8206      * Sets the initialize size for the resizing element
8207      * @param {Number} size The initial size
8208      */
8209     setCurrentSize : function(size){
8210         var oldAnimate = this.animate;
8211         this.animate = false;
8212         this.adapter.setElementSize(this, size);
8213         this.animate = oldAnimate;
8214     },
8215     
8216     /**
8217      * Destroy this splitbar. 
8218      * @param {Boolean} removeEl True to remove the element
8219      */
8220     destroy : function(removeEl){
8221         if(this.shim){
8222             this.shim.remove();
8223         }
8224         this.dd.unreg();
8225         this.proxy.parentNode.removeChild(this.proxy);
8226         if(removeEl){
8227             this.el.remove();
8228         }
8229     }
8230 });
8231
8232 /**
8233  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8234  */
8235 Roo.SplitBar.createProxy = function(dir){
8236     var proxy = new Roo.Element(document.createElement("div"));
8237     proxy.unselectable();
8238     var cls = 'x-splitbar-proxy';
8239     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8240     document.body.appendChild(proxy.dom);
8241     return proxy.dom;
8242 };
8243
8244 /** 
8245  * @class Roo.SplitBar.BasicLayoutAdapter
8246  * Default Adapter. It assumes the splitter and resizing element are not positioned
8247  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8248  */
8249 Roo.SplitBar.BasicLayoutAdapter = function(){
8250 };
8251
8252 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8253     // do nothing for now
8254     init : function(s){
8255     
8256     },
8257     /**
8258      * Called before drag operations to get the current size of the resizing element. 
8259      * @param {Roo.SplitBar} s The SplitBar using this adapter
8260      */
8261      getElementSize : function(s){
8262         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8263             return s.resizingEl.getWidth();
8264         }else{
8265             return s.resizingEl.getHeight();
8266         }
8267     },
8268     
8269     /**
8270      * Called after drag operations to set the size of the resizing element.
8271      * @param {Roo.SplitBar} s The SplitBar using this adapter
8272      * @param {Number} newSize The new size to set
8273      * @param {Function} onComplete A function to be invoked when resizing is complete
8274      */
8275     setElementSize : function(s, newSize, onComplete){
8276         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8277             if(!s.animate){
8278                 s.resizingEl.setWidth(newSize);
8279                 if(onComplete){
8280                     onComplete(s, newSize);
8281                 }
8282             }else{
8283                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8284             }
8285         }else{
8286             
8287             if(!s.animate){
8288                 s.resizingEl.setHeight(newSize);
8289                 if(onComplete){
8290                     onComplete(s, newSize);
8291                 }
8292             }else{
8293                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8294             }
8295         }
8296     }
8297 };
8298
8299 /** 
8300  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8301  * @extends Roo.SplitBar.BasicLayoutAdapter
8302  * Adapter that  moves the splitter element to align with the resized sizing element. 
8303  * Used with an absolute positioned SplitBar.
8304  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8305  * document.body, make sure you assign an id to the body element.
8306  */
8307 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8308     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8309     this.container = Roo.get(container);
8310 };
8311
8312 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8313     init : function(s){
8314         this.basic.init(s);
8315     },
8316     
8317     getElementSize : function(s){
8318         return this.basic.getElementSize(s);
8319     },
8320     
8321     setElementSize : function(s, newSize, onComplete){
8322         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8323     },
8324     
8325     moveSplitter : function(s){
8326         var yes = Roo.SplitBar;
8327         switch(s.placement){
8328             case yes.LEFT:
8329                 s.el.setX(s.resizingEl.getRight());
8330                 break;
8331             case yes.RIGHT:
8332                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8333                 break;
8334             case yes.TOP:
8335                 s.el.setY(s.resizingEl.getBottom());
8336                 break;
8337             case yes.BOTTOM:
8338                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8339                 break;
8340         }
8341     }
8342 };
8343
8344 /**
8345  * Orientation constant - Create a vertical SplitBar
8346  * @static
8347  * @type Number
8348  */
8349 Roo.SplitBar.VERTICAL = 1;
8350
8351 /**
8352  * Orientation constant - Create a horizontal SplitBar
8353  * @static
8354  * @type Number
8355  */
8356 Roo.SplitBar.HORIZONTAL = 2;
8357
8358 /**
8359  * Placement constant - The resizing element is to the left of the splitter element
8360  * @static
8361  * @type Number
8362  */
8363 Roo.SplitBar.LEFT = 1;
8364
8365 /**
8366  * Placement constant - The resizing element is to the right of the splitter element
8367  * @static
8368  * @type Number
8369  */
8370 Roo.SplitBar.RIGHT = 2;
8371
8372 /**
8373  * Placement constant - The resizing element is positioned above the splitter element
8374  * @static
8375  * @type Number
8376  */
8377 Roo.SplitBar.TOP = 3;
8378
8379 /**
8380  * Placement constant - The resizing element is positioned under splitter element
8381  * @static
8382  * @type Number
8383  */
8384 Roo.SplitBar.BOTTOM = 4;
8385 /*
8386  * Based on:
8387  * Ext JS Library 1.1.1
8388  * Copyright(c) 2006-2007, Ext JS, LLC.
8389  *
8390  * Originally Released Under LGPL - original licence link has changed is not relivant.
8391  *
8392  * Fork - LGPL
8393  * <script type="text/javascript">
8394  */
8395
8396 /**
8397  * @class Roo.View
8398  * @extends Roo.util.Observable
8399  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8400  * This class also supports single and multi selection modes. <br>
8401  * Create a data model bound view:
8402  <pre><code>
8403  var store = new Roo.data.Store(...);
8404
8405  var view = new Roo.View({
8406     el : "my-element",
8407     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8408  
8409     singleSelect: true,
8410     selectedClass: "ydataview-selected",
8411     store: store
8412  });
8413
8414  // listen for node click?
8415  view.on("click", function(vw, index, node, e){
8416  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8417  });
8418
8419  // load XML data
8420  dataModel.load("foobar.xml");
8421  </code></pre>
8422  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8423  * <br><br>
8424  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8425  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8426  * 
8427  * Note: old style constructor is still suported (container, template, config)
8428  * 
8429  * @constructor
8430  * Create a new View
8431  * @param {Object} config The config object
8432  * 
8433  */
8434 Roo.View = function(config, depreciated_tpl, depreciated_config){
8435     
8436     this.parent = false;
8437     
8438     if (typeof(depreciated_tpl) == 'undefined') {
8439         // new way.. - universal constructor.
8440         Roo.apply(this, config);
8441         this.el  = Roo.get(this.el);
8442     } else {
8443         // old format..
8444         this.el  = Roo.get(config);
8445         this.tpl = depreciated_tpl;
8446         Roo.apply(this, depreciated_config);
8447     }
8448     this.wrapEl  = this.el.wrap().wrap();
8449     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8450     
8451     
8452     if(typeof(this.tpl) == "string"){
8453         this.tpl = new Roo.Template(this.tpl);
8454     } else {
8455         // support xtype ctors..
8456         this.tpl = new Roo.factory(this.tpl, Roo);
8457     }
8458     
8459     
8460     this.tpl.compile();
8461     
8462     /** @private */
8463     this.addEvents({
8464         /**
8465          * @event beforeclick
8466          * Fires before a click is processed. Returns false to cancel the default action.
8467          * @param {Roo.View} this
8468          * @param {Number} index The index of the target node
8469          * @param {HTMLElement} node The target node
8470          * @param {Roo.EventObject} e The raw event object
8471          */
8472             "beforeclick" : true,
8473         /**
8474          * @event click
8475          * Fires when a template node is clicked.
8476          * @param {Roo.View} this
8477          * @param {Number} index The index of the target node
8478          * @param {HTMLElement} node The target node
8479          * @param {Roo.EventObject} e The raw event object
8480          */
8481             "click" : true,
8482         /**
8483          * @event dblclick
8484          * Fires when a template node is double clicked.
8485          * @param {Roo.View} this
8486          * @param {Number} index The index of the target node
8487          * @param {HTMLElement} node The target node
8488          * @param {Roo.EventObject} e The raw event object
8489          */
8490             "dblclick" : true,
8491         /**
8492          * @event contextmenu
8493          * Fires when a template node is right clicked.
8494          * @param {Roo.View} this
8495          * @param {Number} index The index of the target node
8496          * @param {HTMLElement} node The target node
8497          * @param {Roo.EventObject} e The raw event object
8498          */
8499             "contextmenu" : true,
8500         /**
8501          * @event selectionchange
8502          * Fires when the selected nodes change.
8503          * @param {Roo.View} this
8504          * @param {Array} selections Array of the selected nodes
8505          */
8506             "selectionchange" : true,
8507     
8508         /**
8509          * @event beforeselect
8510          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8511          * @param {Roo.View} this
8512          * @param {HTMLElement} node The node to be selected
8513          * @param {Array} selections Array of currently selected nodes
8514          */
8515             "beforeselect" : true,
8516         /**
8517          * @event preparedata
8518          * Fires on every row to render, to allow you to change the data.
8519          * @param {Roo.View} this
8520          * @param {Object} data to be rendered (change this)
8521          */
8522           "preparedata" : true
8523           
8524           
8525         });
8526
8527
8528
8529     this.el.on({
8530         "click": this.onClick,
8531         "dblclick": this.onDblClick,
8532         "contextmenu": this.onContextMenu,
8533         scope:this
8534     });
8535
8536     this.selections = [];
8537     this.nodes = [];
8538     this.cmp = new Roo.CompositeElementLite([]);
8539     if(this.store){
8540         this.store = Roo.factory(this.store, Roo.data);
8541         this.setStore(this.store, true);
8542     }
8543     
8544     if ( this.footer && this.footer.xtype) {
8545            
8546          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8547         
8548         this.footer.dataSource = this.store;
8549         this.footer.container = fctr;
8550         this.footer = Roo.factory(this.footer, Roo);
8551         fctr.insertFirst(this.el);
8552         
8553         // this is a bit insane - as the paging toolbar seems to detach the el..
8554 //        dom.parentNode.parentNode.parentNode
8555          // they get detached?
8556     }
8557     
8558     
8559     Roo.View.superclass.constructor.call(this);
8560     
8561     
8562 };
8563
8564 Roo.extend(Roo.View, Roo.util.Observable, {
8565     
8566      /**
8567      * @cfg {Roo.data.Store} store Data store to load data from.
8568      */
8569     store : false,
8570     
8571     /**
8572      * @cfg {String|Roo.Element} el The container element.
8573      */
8574     el : '',
8575     
8576     /**
8577      * @cfg {String|Roo.Template} tpl The template used by this View 
8578      */
8579     tpl : false,
8580     /**
8581      * @cfg {String} dataName the named area of the template to use as the data area
8582      *                          Works with domtemplates roo-name="name"
8583      */
8584     dataName: false,
8585     /**
8586      * @cfg {String} selectedClass The css class to add to selected nodes
8587      */
8588     selectedClass : "x-view-selected",
8589      /**
8590      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8591      */
8592     emptyText : "",
8593     
8594     /**
8595      * @cfg {String} text to display on mask (default Loading)
8596      */
8597     mask : false,
8598     /**
8599      * @cfg {Boolean} multiSelect Allow multiple selection
8600      */
8601     multiSelect : false,
8602     /**
8603      * @cfg {Boolean} singleSelect Allow single selection
8604      */
8605     singleSelect:  false,
8606     
8607     /**
8608      * @cfg {Boolean} toggleSelect - selecting 
8609      */
8610     toggleSelect : false,
8611     
8612     /**
8613      * @cfg {Boolean} tickable - selecting 
8614      */
8615     tickable : false,
8616     
8617     /**
8618      * Returns the element this view is bound to.
8619      * @return {Roo.Element}
8620      */
8621     getEl : function(){
8622         return this.wrapEl;
8623     },
8624     
8625     
8626
8627     /**
8628      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8629      */
8630     refresh : function(){
8631         //Roo.log('refresh');
8632         var t = this.tpl;
8633         
8634         // if we are using something like 'domtemplate', then
8635         // the what gets used is:
8636         // t.applySubtemplate(NAME, data, wrapping data..)
8637         // the outer template then get' applied with
8638         //     the store 'extra data'
8639         // and the body get's added to the
8640         //      roo-name="data" node?
8641         //      <span class='roo-tpl-{name}'></span> ?????
8642         
8643         
8644         
8645         this.clearSelections();
8646         this.el.update("");
8647         var html = [];
8648         var records = this.store.getRange();
8649         if(records.length < 1) {
8650             
8651             // is this valid??  = should it render a template??
8652             
8653             this.el.update(this.emptyText);
8654             return;
8655         }
8656         var el = this.el;
8657         if (this.dataName) {
8658             this.el.update(t.apply(this.store.meta)); //????
8659             el = this.el.child('.roo-tpl-' + this.dataName);
8660         }
8661         
8662         for(var i = 0, len = records.length; i < len; i++){
8663             var data = this.prepareData(records[i].data, i, records[i]);
8664             this.fireEvent("preparedata", this, data, i, records[i]);
8665             
8666             var d = Roo.apply({}, data);
8667             
8668             if(this.tickable){
8669                 Roo.apply(d, {'roo-id' : Roo.id()});
8670                 
8671                 var _this = this;
8672             
8673                 Roo.each(this.parent.item, function(item){
8674                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8675                         return;
8676                     }
8677                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8678                 });
8679             }
8680             
8681             html[html.length] = Roo.util.Format.trim(
8682                 this.dataName ?
8683                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8684                     t.apply(d)
8685             );
8686         }
8687         
8688         
8689         
8690         el.update(html.join(""));
8691         this.nodes = el.dom.childNodes;
8692         this.updateIndexes(0);
8693     },
8694     
8695
8696     /**
8697      * Function to override to reformat the data that is sent to
8698      * the template for each node.
8699      * DEPRICATED - use the preparedata event handler.
8700      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8701      * a JSON object for an UpdateManager bound view).
8702      */
8703     prepareData : function(data, index, record)
8704     {
8705         this.fireEvent("preparedata", this, data, index, record);
8706         return data;
8707     },
8708
8709     onUpdate : function(ds, record){
8710         // Roo.log('on update');   
8711         this.clearSelections();
8712         var index = this.store.indexOf(record);
8713         var n = this.nodes[index];
8714         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8715         n.parentNode.removeChild(n);
8716         this.updateIndexes(index, index);
8717     },
8718
8719     
8720     
8721 // --------- FIXME     
8722     onAdd : function(ds, records, index)
8723     {
8724         //Roo.log(['on Add', ds, records, index] );        
8725         this.clearSelections();
8726         if(this.nodes.length == 0){
8727             this.refresh();
8728             return;
8729         }
8730         var n = this.nodes[index];
8731         for(var i = 0, len = records.length; i < len; i++){
8732             var d = this.prepareData(records[i].data, i, records[i]);
8733             if(n){
8734                 this.tpl.insertBefore(n, d);
8735             }else{
8736                 
8737                 this.tpl.append(this.el, d);
8738             }
8739         }
8740         this.updateIndexes(index);
8741     },
8742
8743     onRemove : function(ds, record, index){
8744        // Roo.log('onRemove');
8745         this.clearSelections();
8746         var el = this.dataName  ?
8747             this.el.child('.roo-tpl-' + this.dataName) :
8748             this.el; 
8749         
8750         el.dom.removeChild(this.nodes[index]);
8751         this.updateIndexes(index);
8752     },
8753
8754     /**
8755      * Refresh an individual node.
8756      * @param {Number} index
8757      */
8758     refreshNode : function(index){
8759         this.onUpdate(this.store, this.store.getAt(index));
8760     },
8761
8762     updateIndexes : function(startIndex, endIndex){
8763         var ns = this.nodes;
8764         startIndex = startIndex || 0;
8765         endIndex = endIndex || ns.length - 1;
8766         for(var i = startIndex; i <= endIndex; i++){
8767             ns[i].nodeIndex = i;
8768         }
8769     },
8770
8771     /**
8772      * Changes the data store this view uses and refresh the view.
8773      * @param {Store} store
8774      */
8775     setStore : function(store, initial){
8776         if(!initial && this.store){
8777             this.store.un("datachanged", this.refresh);
8778             this.store.un("add", this.onAdd);
8779             this.store.un("remove", this.onRemove);
8780             this.store.un("update", this.onUpdate);
8781             this.store.un("clear", this.refresh);
8782             this.store.un("beforeload", this.onBeforeLoad);
8783             this.store.un("load", this.onLoad);
8784             this.store.un("loadexception", this.onLoad);
8785         }
8786         if(store){
8787           
8788             store.on("datachanged", this.refresh, this);
8789             store.on("add", this.onAdd, this);
8790             store.on("remove", this.onRemove, this);
8791             store.on("update", this.onUpdate, this);
8792             store.on("clear", this.refresh, this);
8793             store.on("beforeload", this.onBeforeLoad, this);
8794             store.on("load", this.onLoad, this);
8795             store.on("loadexception", this.onLoad, this);
8796         }
8797         
8798         if(store){
8799             this.refresh();
8800         }
8801     },
8802     /**
8803      * onbeforeLoad - masks the loading area.
8804      *
8805      */
8806     onBeforeLoad : function(store,opts)
8807     {
8808          //Roo.log('onBeforeLoad');   
8809         if (!opts.add) {
8810             this.el.update("");
8811         }
8812         this.el.mask(this.mask ? this.mask : "Loading" ); 
8813     },
8814     onLoad : function ()
8815     {
8816         this.el.unmask();
8817     },
8818     
8819
8820     /**
8821      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8822      * @param {HTMLElement} node
8823      * @return {HTMLElement} The template node
8824      */
8825     findItemFromChild : function(node){
8826         var el = this.dataName  ?
8827             this.el.child('.roo-tpl-' + this.dataName,true) :
8828             this.el.dom; 
8829         
8830         if(!node || node.parentNode == el){
8831                     return node;
8832             }
8833             var p = node.parentNode;
8834             while(p && p != el){
8835             if(p.parentNode == el){
8836                 return p;
8837             }
8838             p = p.parentNode;
8839         }
8840             return null;
8841     },
8842
8843     /** @ignore */
8844     onClick : function(e){
8845         var item = this.findItemFromChild(e.getTarget());
8846         if(item){
8847             var index = this.indexOf(item);
8848             if(this.onItemClick(item, index, e) !== false){
8849                 this.fireEvent("click", this, index, item, e);
8850             }
8851         }else{
8852             this.clearSelections();
8853         }
8854     },
8855
8856     /** @ignore */
8857     onContextMenu : function(e){
8858         var item = this.findItemFromChild(e.getTarget());
8859         if(item){
8860             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8861         }
8862     },
8863
8864     /** @ignore */
8865     onDblClick : function(e){
8866         var item = this.findItemFromChild(e.getTarget());
8867         if(item){
8868             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8869         }
8870     },
8871
8872     onItemClick : function(item, index, e)
8873     {
8874         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8875             return false;
8876         }
8877         if (this.toggleSelect) {
8878             var m = this.isSelected(item) ? 'unselect' : 'select';
8879             //Roo.log(m);
8880             var _t = this;
8881             _t[m](item, true, false);
8882             return true;
8883         }
8884         if(this.multiSelect || this.singleSelect){
8885             if(this.multiSelect && e.shiftKey && this.lastSelection){
8886                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8887             }else{
8888                 this.select(item, this.multiSelect && e.ctrlKey);
8889                 this.lastSelection = item;
8890             }
8891             
8892             if(!this.tickable){
8893                 e.preventDefault();
8894             }
8895             
8896         }
8897         return true;
8898     },
8899
8900     /**
8901      * Get the number of selected nodes.
8902      * @return {Number}
8903      */
8904     getSelectionCount : function(){
8905         return this.selections.length;
8906     },
8907
8908     /**
8909      * Get the currently selected nodes.
8910      * @return {Array} An array of HTMLElements
8911      */
8912     getSelectedNodes : function(){
8913         return this.selections;
8914     },
8915
8916     /**
8917      * Get the indexes of the selected nodes.
8918      * @return {Array}
8919      */
8920     getSelectedIndexes : function(){
8921         var indexes = [], s = this.selections;
8922         for(var i = 0, len = s.length; i < len; i++){
8923             indexes.push(s[i].nodeIndex);
8924         }
8925         return indexes;
8926     },
8927
8928     /**
8929      * Clear all selections
8930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8931      */
8932     clearSelections : function(suppressEvent){
8933         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8934             this.cmp.elements = this.selections;
8935             this.cmp.removeClass(this.selectedClass);
8936             this.selections = [];
8937             if(!suppressEvent){
8938                 this.fireEvent("selectionchange", this, this.selections);
8939             }
8940         }
8941     },
8942
8943     /**
8944      * Returns true if the passed node is selected
8945      * @param {HTMLElement/Number} node The node or node index
8946      * @return {Boolean}
8947      */
8948     isSelected : function(node){
8949         var s = this.selections;
8950         if(s.length < 1){
8951             return false;
8952         }
8953         node = this.getNode(node);
8954         return s.indexOf(node) !== -1;
8955     },
8956
8957     /**
8958      * Selects nodes.
8959      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8960      * @param {Boolean} keepExisting (optional) true to keep existing selections
8961      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8962      */
8963     select : function(nodeInfo, keepExisting, suppressEvent){
8964         if(nodeInfo instanceof Array){
8965             if(!keepExisting){
8966                 this.clearSelections(true);
8967             }
8968             for(var i = 0, len = nodeInfo.length; i < len; i++){
8969                 this.select(nodeInfo[i], true, true);
8970             }
8971             return;
8972         } 
8973         var node = this.getNode(nodeInfo);
8974         if(!node || this.isSelected(node)){
8975             return; // already selected.
8976         }
8977         if(!keepExisting){
8978             this.clearSelections(true);
8979         }
8980         
8981         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8982             Roo.fly(node).addClass(this.selectedClass);
8983             this.selections.push(node);
8984             if(!suppressEvent){
8985                 this.fireEvent("selectionchange", this, this.selections);
8986             }
8987         }
8988         
8989         
8990     },
8991       /**
8992      * Unselects nodes.
8993      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8994      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8995      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8996      */
8997     unselect : function(nodeInfo, keepExisting, suppressEvent)
8998     {
8999         if(nodeInfo instanceof Array){
9000             Roo.each(this.selections, function(s) {
9001                 this.unselect(s, nodeInfo);
9002             }, this);
9003             return;
9004         }
9005         var node = this.getNode(nodeInfo);
9006         if(!node || !this.isSelected(node)){
9007             //Roo.log("not selected");
9008             return; // not selected.
9009         }
9010         // fireevent???
9011         var ns = [];
9012         Roo.each(this.selections, function(s) {
9013             if (s == node ) {
9014                 Roo.fly(node).removeClass(this.selectedClass);
9015
9016                 return;
9017             }
9018             ns.push(s);
9019         },this);
9020         
9021         this.selections= ns;
9022         this.fireEvent("selectionchange", this, this.selections);
9023     },
9024
9025     /**
9026      * Gets a template node.
9027      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9028      * @return {HTMLElement} The node or null if it wasn't found
9029      */
9030     getNode : function(nodeInfo){
9031         if(typeof nodeInfo == "string"){
9032             return document.getElementById(nodeInfo);
9033         }else if(typeof nodeInfo == "number"){
9034             return this.nodes[nodeInfo];
9035         }
9036         return nodeInfo;
9037     },
9038
9039     /**
9040      * Gets a range template nodes.
9041      * @param {Number} startIndex
9042      * @param {Number} endIndex
9043      * @return {Array} An array of nodes
9044      */
9045     getNodes : function(start, end){
9046         var ns = this.nodes;
9047         start = start || 0;
9048         end = typeof end == "undefined" ? ns.length - 1 : end;
9049         var nodes = [];
9050         if(start <= end){
9051             for(var i = start; i <= end; i++){
9052                 nodes.push(ns[i]);
9053             }
9054         } else{
9055             for(var i = start; i >= end; i--){
9056                 nodes.push(ns[i]);
9057             }
9058         }
9059         return nodes;
9060     },
9061
9062     /**
9063      * Finds the index of the passed node
9064      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9065      * @return {Number} The index of the node or -1
9066      */
9067     indexOf : function(node){
9068         node = this.getNode(node);
9069         if(typeof node.nodeIndex == "number"){
9070             return node.nodeIndex;
9071         }
9072         var ns = this.nodes;
9073         for(var i = 0, len = ns.length; i < len; i++){
9074             if(ns[i] == node){
9075                 return i;
9076             }
9077         }
9078         return -1;
9079     }
9080 });
9081 /*
9082  * Based on:
9083  * Ext JS Library 1.1.1
9084  * Copyright(c) 2006-2007, Ext JS, LLC.
9085  *
9086  * Originally Released Under LGPL - original licence link has changed is not relivant.
9087  *
9088  * Fork - LGPL
9089  * <script type="text/javascript">
9090  */
9091
9092 /**
9093  * @class Roo.JsonView
9094  * @extends Roo.View
9095  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9096 <pre><code>
9097 var view = new Roo.JsonView({
9098     container: "my-element",
9099     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9100     multiSelect: true, 
9101     jsonRoot: "data" 
9102 });
9103
9104 // listen for node click?
9105 view.on("click", function(vw, index, node, e){
9106     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9107 });
9108
9109 // direct load of JSON data
9110 view.load("foobar.php");
9111
9112 // Example from my blog list
9113 var tpl = new Roo.Template(
9114     '&lt;div class="entry"&gt;' +
9115     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9116     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9117     "&lt;/div&gt;&lt;hr /&gt;"
9118 );
9119
9120 var moreView = new Roo.JsonView({
9121     container :  "entry-list", 
9122     template : tpl,
9123     jsonRoot: "posts"
9124 });
9125 moreView.on("beforerender", this.sortEntries, this);
9126 moreView.load({
9127     url: "/blog/get-posts.php",
9128     params: "allposts=true",
9129     text: "Loading Blog Entries..."
9130 });
9131 </code></pre>
9132
9133 * Note: old code is supported with arguments : (container, template, config)
9134
9135
9136  * @constructor
9137  * Create a new JsonView
9138  * 
9139  * @param {Object} config The config object
9140  * 
9141  */
9142 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9143     
9144     
9145     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9146
9147     var um = this.el.getUpdateManager();
9148     um.setRenderer(this);
9149     um.on("update", this.onLoad, this);
9150     um.on("failure", this.onLoadException, this);
9151
9152     /**
9153      * @event beforerender
9154      * Fires before rendering of the downloaded JSON data.
9155      * @param {Roo.JsonView} this
9156      * @param {Object} data The JSON data loaded
9157      */
9158     /**
9159      * @event load
9160      * Fires when data is loaded.
9161      * @param {Roo.JsonView} this
9162      * @param {Object} data The JSON data loaded
9163      * @param {Object} response The raw Connect response object
9164      */
9165     /**
9166      * @event loadexception
9167      * Fires when loading fails.
9168      * @param {Roo.JsonView} this
9169      * @param {Object} response The raw Connect response object
9170      */
9171     this.addEvents({
9172         'beforerender' : true,
9173         'load' : true,
9174         'loadexception' : true
9175     });
9176 };
9177 Roo.extend(Roo.JsonView, Roo.View, {
9178     /**
9179      * @type {String} The root property in the loaded JSON object that contains the data
9180      */
9181     jsonRoot : "",
9182
9183     /**
9184      * Refreshes the view.
9185      */
9186     refresh : function(){
9187         this.clearSelections();
9188         this.el.update("");
9189         var html = [];
9190         var o = this.jsonData;
9191         if(o && o.length > 0){
9192             for(var i = 0, len = o.length; i < len; i++){
9193                 var data = this.prepareData(o[i], i, o);
9194                 html[html.length] = this.tpl.apply(data);
9195             }
9196         }else{
9197             html.push(this.emptyText);
9198         }
9199         this.el.update(html.join(""));
9200         this.nodes = this.el.dom.childNodes;
9201         this.updateIndexes(0);
9202     },
9203
9204     /**
9205      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9206      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9207      <pre><code>
9208      view.load({
9209          url: "your-url.php",
9210          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9211          callback: yourFunction,
9212          scope: yourObject, //(optional scope)
9213          discardUrl: false,
9214          nocache: false,
9215          text: "Loading...",
9216          timeout: 30,
9217          scripts: false
9218      });
9219      </code></pre>
9220      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9221      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9222      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9223      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9224      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9225      */
9226     load : function(){
9227         var um = this.el.getUpdateManager();
9228         um.update.apply(um, arguments);
9229     },
9230
9231     render : function(el, response){
9232         this.clearSelections();
9233         this.el.update("");
9234         var o;
9235         try{
9236             o = Roo.util.JSON.decode(response.responseText);
9237             if(this.jsonRoot){
9238                 
9239                 o = o[this.jsonRoot];
9240             }
9241         } catch(e){
9242         }
9243         /**
9244          * The current JSON data or null
9245          */
9246         this.jsonData = o;
9247         this.beforeRender();
9248         this.refresh();
9249     },
9250
9251 /**
9252  * Get the number of records in the current JSON dataset
9253  * @return {Number}
9254  */
9255     getCount : function(){
9256         return this.jsonData ? this.jsonData.length : 0;
9257     },
9258
9259 /**
9260  * Returns the JSON object for the specified node(s)
9261  * @param {HTMLElement/Array} node The node or an array of nodes
9262  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9263  * you get the JSON object for the node
9264  */
9265     getNodeData : function(node){
9266         if(node instanceof Array){
9267             var data = [];
9268             for(var i = 0, len = node.length; i < len; i++){
9269                 data.push(this.getNodeData(node[i]));
9270             }
9271             return data;
9272         }
9273         return this.jsonData[this.indexOf(node)] || null;
9274     },
9275
9276     beforeRender : function(){
9277         this.snapshot = this.jsonData;
9278         if(this.sortInfo){
9279             this.sort.apply(this, this.sortInfo);
9280         }
9281         this.fireEvent("beforerender", this, this.jsonData);
9282     },
9283
9284     onLoad : function(el, o){
9285         this.fireEvent("load", this, this.jsonData, o);
9286     },
9287
9288     onLoadException : function(el, o){
9289         this.fireEvent("loadexception", this, o);
9290     },
9291
9292 /**
9293  * Filter the data by a specific property.
9294  * @param {String} property A property on your JSON objects
9295  * @param {String/RegExp} value Either string that the property values
9296  * should start with, or a RegExp to test against the property
9297  */
9298     filter : function(property, value){
9299         if(this.jsonData){
9300             var data = [];
9301             var ss = this.snapshot;
9302             if(typeof value == "string"){
9303                 var vlen = value.length;
9304                 if(vlen == 0){
9305                     this.clearFilter();
9306                     return;
9307                 }
9308                 value = value.toLowerCase();
9309                 for(var i = 0, len = ss.length; i < len; i++){
9310                     var o = ss[i];
9311                     if(o[property].substr(0, vlen).toLowerCase() == value){
9312                         data.push(o);
9313                     }
9314                 }
9315             } else if(value.exec){ // regex?
9316                 for(var i = 0, len = ss.length; i < len; i++){
9317                     var o = ss[i];
9318                     if(value.test(o[property])){
9319                         data.push(o);
9320                     }
9321                 }
9322             } else{
9323                 return;
9324             }
9325             this.jsonData = data;
9326             this.refresh();
9327         }
9328     },
9329
9330 /**
9331  * Filter by a function. The passed function will be called with each
9332  * object in the current dataset. If the function returns true the value is kept,
9333  * otherwise it is filtered.
9334  * @param {Function} fn
9335  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9336  */
9337     filterBy : function(fn, scope){
9338         if(this.jsonData){
9339             var data = [];
9340             var ss = this.snapshot;
9341             for(var i = 0, len = ss.length; i < len; i++){
9342                 var o = ss[i];
9343                 if(fn.call(scope || this, o)){
9344                     data.push(o);
9345                 }
9346             }
9347             this.jsonData = data;
9348             this.refresh();
9349         }
9350     },
9351
9352 /**
9353  * Clears the current filter.
9354  */
9355     clearFilter : function(){
9356         if(this.snapshot && this.jsonData != this.snapshot){
9357             this.jsonData = this.snapshot;
9358             this.refresh();
9359         }
9360     },
9361
9362
9363 /**
9364  * Sorts the data for this view and refreshes it.
9365  * @param {String} property A property on your JSON objects to sort on
9366  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9367  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9368  */
9369     sort : function(property, dir, sortType){
9370         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9371         if(this.jsonData){
9372             var p = property;
9373             var dsc = dir && dir.toLowerCase() == "desc";
9374             var f = function(o1, o2){
9375                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9376                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9377                 ;
9378                 if(v1 < v2){
9379                     return dsc ? +1 : -1;
9380                 } else if(v1 > v2){
9381                     return dsc ? -1 : +1;
9382                 } else{
9383                     return 0;
9384                 }
9385             };
9386             this.jsonData.sort(f);
9387             this.refresh();
9388             if(this.jsonData != this.snapshot){
9389                 this.snapshot.sort(f);
9390             }
9391         }
9392     }
9393 });/*
9394  * Based on:
9395  * Ext JS Library 1.1.1
9396  * Copyright(c) 2006-2007, Ext JS, LLC.
9397  *
9398  * Originally Released Under LGPL - original licence link has changed is not relivant.
9399  *
9400  * Fork - LGPL
9401  * <script type="text/javascript">
9402  */
9403  
9404
9405 /**
9406  * @class Roo.ColorPalette
9407  * @extends Roo.Component
9408  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9409  * Here's an example of typical usage:
9410  * <pre><code>
9411 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9412 cp.render('my-div');
9413
9414 cp.on('select', function(palette, selColor){
9415     // do something with selColor
9416 });
9417 </code></pre>
9418  * @constructor
9419  * Create a new ColorPalette
9420  * @param {Object} config The config object
9421  */
9422 Roo.ColorPalette = function(config){
9423     Roo.ColorPalette.superclass.constructor.call(this, config);
9424     this.addEvents({
9425         /**
9426              * @event select
9427              * Fires when a color is selected
9428              * @param {ColorPalette} this
9429              * @param {String} color The 6-digit color hex code (without the # symbol)
9430              */
9431         select: true
9432     });
9433
9434     if(this.handler){
9435         this.on("select", this.handler, this.scope, true);
9436     }
9437 };
9438 Roo.extend(Roo.ColorPalette, Roo.Component, {
9439     /**
9440      * @cfg {String} itemCls
9441      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9442      */
9443     itemCls : "x-color-palette",
9444     /**
9445      * @cfg {String} value
9446      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9447      * the hex codes are case-sensitive.
9448      */
9449     value : null,
9450     clickEvent:'click',
9451     // private
9452     ctype: "Roo.ColorPalette",
9453
9454     /**
9455      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9456      */
9457     allowReselect : false,
9458
9459     /**
9460      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9461      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9462      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9463      * of colors with the width setting until the box is symmetrical.</p>
9464      * <p>You can override individual colors if needed:</p>
9465      * <pre><code>
9466 var cp = new Roo.ColorPalette();
9467 cp.colors[0] = "FF0000";  // change the first box to red
9468 </code></pre>
9469
9470 Or you can provide a custom array of your own for complete control:
9471 <pre><code>
9472 var cp = new Roo.ColorPalette();
9473 cp.colors = ["000000", "993300", "333300"];
9474 </code></pre>
9475      * @type Array
9476      */
9477     colors : [
9478         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9479         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9480         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9481         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9482         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9483     ],
9484
9485     // private
9486     onRender : function(container, position){
9487         var t = new Roo.MasterTemplate(
9488             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9489         );
9490         var c = this.colors;
9491         for(var i = 0, len = c.length; i < len; i++){
9492             t.add([c[i]]);
9493         }
9494         var el = document.createElement("div");
9495         el.className = this.itemCls;
9496         t.overwrite(el);
9497         container.dom.insertBefore(el, position);
9498         this.el = Roo.get(el);
9499         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9500         if(this.clickEvent != 'click'){
9501             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9502         }
9503     },
9504
9505     // private
9506     afterRender : function(){
9507         Roo.ColorPalette.superclass.afterRender.call(this);
9508         if(this.value){
9509             var s = this.value;
9510             this.value = null;
9511             this.select(s);
9512         }
9513     },
9514
9515     // private
9516     handleClick : function(e, t){
9517         e.preventDefault();
9518         if(!this.disabled){
9519             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9520             this.select(c.toUpperCase());
9521         }
9522     },
9523
9524     /**
9525      * Selects the specified color in the palette (fires the select event)
9526      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9527      */
9528     select : function(color){
9529         color = color.replace("#", "");
9530         if(color != this.value || this.allowReselect){
9531             var el = this.el;
9532             if(this.value){
9533                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9534             }
9535             el.child("a.color-"+color).addClass("x-color-palette-sel");
9536             this.value = color;
9537             this.fireEvent("select", this, color);
9538         }
9539     }
9540 });/*
9541  * Based on:
9542  * Ext JS Library 1.1.1
9543  * Copyright(c) 2006-2007, Ext JS, LLC.
9544  *
9545  * Originally Released Under LGPL - original licence link has changed is not relivant.
9546  *
9547  * Fork - LGPL
9548  * <script type="text/javascript">
9549  */
9550  
9551 /**
9552  * @class Roo.DatePicker
9553  * @extends Roo.Component
9554  * Simple date picker class.
9555  * @constructor
9556  * Create a new DatePicker
9557  * @param {Object} config The config object
9558  */
9559 Roo.DatePicker = function(config){
9560     Roo.DatePicker.superclass.constructor.call(this, config);
9561
9562     this.value = config && config.value ?
9563                  config.value.clearTime() : new Date().clearTime();
9564
9565     this.addEvents({
9566         /**
9567              * @event select
9568              * Fires when a date is selected
9569              * @param {DatePicker} this
9570              * @param {Date} date The selected date
9571              */
9572         'select': true,
9573         /**
9574              * @event monthchange
9575              * Fires when the displayed month changes 
9576              * @param {DatePicker} this
9577              * @param {Date} date The selected month
9578              */
9579         'monthchange': true
9580     });
9581
9582     if(this.handler){
9583         this.on("select", this.handler,  this.scope || this);
9584     }
9585     // build the disabledDatesRE
9586     if(!this.disabledDatesRE && this.disabledDates){
9587         var dd = this.disabledDates;
9588         var re = "(?:";
9589         for(var i = 0; i < dd.length; i++){
9590             re += dd[i];
9591             if(i != dd.length-1) {
9592                 re += "|";
9593             }
9594         }
9595         this.disabledDatesRE = new RegExp(re + ")");
9596     }
9597 };
9598
9599 Roo.extend(Roo.DatePicker, Roo.Component, {
9600     /**
9601      * @cfg {String} todayText
9602      * The text to display on the button that selects the current date (defaults to "Today")
9603      */
9604     todayText : "Today",
9605     /**
9606      * @cfg {String} okText
9607      * The text to display on the ok button
9608      */
9609     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9610     /**
9611      * @cfg {String} cancelText
9612      * The text to display on the cancel button
9613      */
9614     cancelText : "Cancel",
9615     /**
9616      * @cfg {String} todayTip
9617      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9618      */
9619     todayTip : "{0} (Spacebar)",
9620     /**
9621      * @cfg {Date} minDate
9622      * Minimum allowable date (JavaScript date object, defaults to null)
9623      */
9624     minDate : null,
9625     /**
9626      * @cfg {Date} maxDate
9627      * Maximum allowable date (JavaScript date object, defaults to null)
9628      */
9629     maxDate : null,
9630     /**
9631      * @cfg {String} minText
9632      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9633      */
9634     minText : "This date is before the minimum date",
9635     /**
9636      * @cfg {String} maxText
9637      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9638      */
9639     maxText : "This date is after the maximum date",
9640     /**
9641      * @cfg {String} format
9642      * The default date format string which can be overriden for localization support.  The format must be
9643      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9644      */
9645     format : "m/d/y",
9646     /**
9647      * @cfg {Array} disabledDays
9648      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9649      */
9650     disabledDays : null,
9651     /**
9652      * @cfg {String} disabledDaysText
9653      * The tooltip to display when the date falls on a disabled day (defaults to "")
9654      */
9655     disabledDaysText : "",
9656     /**
9657      * @cfg {RegExp} disabledDatesRE
9658      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9659      */
9660     disabledDatesRE : null,
9661     /**
9662      * @cfg {String} disabledDatesText
9663      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9664      */
9665     disabledDatesText : "",
9666     /**
9667      * @cfg {Boolean} constrainToViewport
9668      * True to constrain the date picker to the viewport (defaults to true)
9669      */
9670     constrainToViewport : true,
9671     /**
9672      * @cfg {Array} monthNames
9673      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9674      */
9675     monthNames : Date.monthNames,
9676     /**
9677      * @cfg {Array} dayNames
9678      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9679      */
9680     dayNames : Date.dayNames,
9681     /**
9682      * @cfg {String} nextText
9683      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9684      */
9685     nextText: 'Next Month (Control+Right)',
9686     /**
9687      * @cfg {String} prevText
9688      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9689      */
9690     prevText: 'Previous Month (Control+Left)',
9691     /**
9692      * @cfg {String} monthYearText
9693      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9694      */
9695     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9696     /**
9697      * @cfg {Number} startDay
9698      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9699      */
9700     startDay : 0,
9701     /**
9702      * @cfg {Bool} showClear
9703      * Show a clear button (usefull for date form elements that can be blank.)
9704      */
9705     
9706     showClear: false,
9707     
9708     /**
9709      * Sets the value of the date field
9710      * @param {Date} value The date to set
9711      */
9712     setValue : function(value){
9713         var old = this.value;
9714         
9715         if (typeof(value) == 'string') {
9716          
9717             value = Date.parseDate(value, this.format);
9718         }
9719         if (!value) {
9720             value = new Date();
9721         }
9722         
9723         this.value = value.clearTime(true);
9724         if(this.el){
9725             this.update(this.value);
9726         }
9727     },
9728
9729     /**
9730      * Gets the current selected value of the date field
9731      * @return {Date} The selected date
9732      */
9733     getValue : function(){
9734         return this.value;
9735     },
9736
9737     // private
9738     focus : function(){
9739         if(this.el){
9740             this.update(this.activeDate);
9741         }
9742     },
9743
9744     // privateval
9745     onRender : function(container, position){
9746         
9747         var m = [
9748              '<table cellspacing="0">',
9749                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
9750                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9751         var dn = this.dayNames;
9752         for(var i = 0; i < 7; i++){
9753             var d = this.startDay+i;
9754             if(d > 6){
9755                 d = d-7;
9756             }
9757             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9758         }
9759         m[m.length] = "</tr></thead><tbody><tr>";
9760         for(var i = 0; i < 42; i++) {
9761             if(i % 7 == 0 && i != 0){
9762                 m[m.length] = "</tr><tr>";
9763             }
9764             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9765         }
9766         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9767             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9768
9769         var el = document.createElement("div");
9770         el.className = "x-date-picker";
9771         el.innerHTML = m.join("");
9772
9773         container.dom.insertBefore(el, position);
9774
9775         this.el = Roo.get(el);
9776         this.eventEl = Roo.get(el.firstChild);
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9779             handler: this.showPrevMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9786             handler: this.showNextMonth,
9787             scope: this,
9788             preventDefault:true,
9789             stopDefault:true
9790         });
9791
9792         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9793
9794         this.monthPicker = this.el.down('div.x-date-mp');
9795         this.monthPicker.enableDisplayMode('block');
9796         
9797         var kn = new Roo.KeyNav(this.eventEl, {
9798             "left" : function(e){
9799                 e.ctrlKey ?
9800                     this.showPrevMonth() :
9801                     this.update(this.activeDate.add("d", -1));
9802             },
9803
9804             "right" : function(e){
9805                 e.ctrlKey ?
9806                     this.showNextMonth() :
9807                     this.update(this.activeDate.add("d", 1));
9808             },
9809
9810             "up" : function(e){
9811                 e.ctrlKey ?
9812                     this.showNextYear() :
9813                     this.update(this.activeDate.add("d", -7));
9814             },
9815
9816             "down" : function(e){
9817                 e.ctrlKey ?
9818                     this.showPrevYear() :
9819                     this.update(this.activeDate.add("d", 7));
9820             },
9821
9822             "pageUp" : function(e){
9823                 this.showNextMonth();
9824             },
9825
9826             "pageDown" : function(e){
9827                 this.showPrevMonth();
9828             },
9829
9830             "enter" : function(e){
9831                 e.stopPropagation();
9832                 return true;
9833             },
9834
9835             scope : this
9836         });
9837
9838         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9839
9840         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9841
9842         this.el.unselectable();
9843         
9844         this.cells = this.el.select("table.x-date-inner tbody td");
9845         this.textNodes = this.el.query("table.x-date-inner tbody span");
9846
9847         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9848             text: "&#160;",
9849             tooltip: this.monthYearText
9850         });
9851
9852         this.mbtn.on('click', this.showMonthPicker, this);
9853         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9854
9855
9856         var today = (new Date()).dateFormat(this.format);
9857         
9858         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9859         if (this.showClear) {
9860             baseTb.add( new Roo.Toolbar.Fill());
9861         }
9862         baseTb.add({
9863             text: String.format(this.todayText, today),
9864             tooltip: String.format(this.todayTip, today),
9865             handler: this.selectToday,
9866             scope: this
9867         });
9868         
9869         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9870             
9871         //});
9872         if (this.showClear) {
9873             
9874             baseTb.add( new Roo.Toolbar.Fill());
9875             baseTb.add({
9876                 text: '&#160;',
9877                 cls: 'x-btn-icon x-btn-clear',
9878                 handler: function() {
9879                     //this.value = '';
9880                     this.fireEvent("select", this, '');
9881                 },
9882                 scope: this
9883             });
9884         }
9885         
9886         
9887         if(Roo.isIE){
9888             this.el.repaint();
9889         }
9890         this.update(this.value);
9891     },
9892
9893     createMonthPicker : function(){
9894         if(!this.monthPicker.dom.firstChild){
9895             var buf = ['<table border="0" cellspacing="0">'];
9896             for(var i = 0; i < 6; i++){
9897                 buf.push(
9898                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9899                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9900                     i == 0 ?
9901                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
9902                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9903                 );
9904             }
9905             buf.push(
9906                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9907                     this.okText,
9908                     '</button><button type="button" class="x-date-mp-cancel">',
9909                     this.cancelText,
9910                     '</button></td></tr>',
9911                 '</table>'
9912             );
9913             this.monthPicker.update(buf.join(''));
9914             this.monthPicker.on('click', this.onMonthClick, this);
9915             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9916
9917             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9918             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9919
9920             this.mpMonths.each(function(m, a, i){
9921                 i += 1;
9922                 if((i%2) == 0){
9923                     m.dom.xmonth = 5 + Math.round(i * .5);
9924                 }else{
9925                     m.dom.xmonth = Math.round((i-1) * .5);
9926                 }
9927             });
9928         }
9929     },
9930
9931     showMonthPicker : function(){
9932         this.createMonthPicker();
9933         var size = this.el.getSize();
9934         this.monthPicker.setSize(size);
9935         this.monthPicker.child('table').setSize(size);
9936
9937         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9938         this.updateMPMonth(this.mpSelMonth);
9939         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9940         this.updateMPYear(this.mpSelYear);
9941
9942         this.monthPicker.slideIn('t', {duration:.2});
9943     },
9944
9945     updateMPYear : function(y){
9946         this.mpyear = y;
9947         var ys = this.mpYears.elements;
9948         for(var i = 1; i <= 10; i++){
9949             var td = ys[i-1], y2;
9950             if((i%2) == 0){
9951                 y2 = y + Math.round(i * .5);
9952                 td.firstChild.innerHTML = y2;
9953                 td.xyear = y2;
9954             }else{
9955                 y2 = y - (5-Math.round(i * .5));
9956                 td.firstChild.innerHTML = y2;
9957                 td.xyear = y2;
9958             }
9959             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9960         }
9961     },
9962
9963     updateMPMonth : function(sm){
9964         this.mpMonths.each(function(m, a, i){
9965             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9966         });
9967     },
9968
9969     selectMPMonth: function(m){
9970         
9971     },
9972
9973     onMonthClick : function(e, t){
9974         e.stopEvent();
9975         var el = new Roo.Element(t), pn;
9976         if(el.is('button.x-date-mp-cancel')){
9977             this.hideMonthPicker();
9978         }
9979         else if(el.is('button.x-date-mp-ok')){
9980             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9981             this.hideMonthPicker();
9982         }
9983         else if(pn = el.up('td.x-date-mp-month', 2)){
9984             this.mpMonths.removeClass('x-date-mp-sel');
9985             pn.addClass('x-date-mp-sel');
9986             this.mpSelMonth = pn.dom.xmonth;
9987         }
9988         else if(pn = el.up('td.x-date-mp-year', 2)){
9989             this.mpYears.removeClass('x-date-mp-sel');
9990             pn.addClass('x-date-mp-sel');
9991             this.mpSelYear = pn.dom.xyear;
9992         }
9993         else if(el.is('a.x-date-mp-prev')){
9994             this.updateMPYear(this.mpyear-10);
9995         }
9996         else if(el.is('a.x-date-mp-next')){
9997             this.updateMPYear(this.mpyear+10);
9998         }
9999     },
10000
10001     onMonthDblClick : function(e, t){
10002         e.stopEvent();
10003         var el = new Roo.Element(t), pn;
10004         if(pn = el.up('td.x-date-mp-month', 2)){
10005             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10006             this.hideMonthPicker();
10007         }
10008         else if(pn = el.up('td.x-date-mp-year', 2)){
10009             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10010             this.hideMonthPicker();
10011         }
10012     },
10013
10014     hideMonthPicker : function(disableAnim){
10015         if(this.monthPicker){
10016             if(disableAnim === true){
10017                 this.monthPicker.hide();
10018             }else{
10019                 this.monthPicker.slideOut('t', {duration:.2});
10020             }
10021         }
10022     },
10023
10024     // private
10025     showPrevMonth : function(e){
10026         this.update(this.activeDate.add("mo", -1));
10027     },
10028
10029     // private
10030     showNextMonth : function(e){
10031         this.update(this.activeDate.add("mo", 1));
10032     },
10033
10034     // private
10035     showPrevYear : function(){
10036         this.update(this.activeDate.add("y", -1));
10037     },
10038
10039     // private
10040     showNextYear : function(){
10041         this.update(this.activeDate.add("y", 1));
10042     },
10043
10044     // private
10045     handleMouseWheel : function(e){
10046         var delta = e.getWheelDelta();
10047         if(delta > 0){
10048             this.showPrevMonth();
10049             e.stopEvent();
10050         } else if(delta < 0){
10051             this.showNextMonth();
10052             e.stopEvent();
10053         }
10054     },
10055
10056     // private
10057     handleDateClick : function(e, t){
10058         e.stopEvent();
10059         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10060             this.setValue(new Date(t.dateValue));
10061             this.fireEvent("select", this, this.value);
10062         }
10063     },
10064
10065     // private
10066     selectToday : function(){
10067         this.setValue(new Date().clearTime());
10068         this.fireEvent("select", this, this.value);
10069     },
10070
10071     // private
10072     update : function(date)
10073     {
10074         var vd = this.activeDate;
10075         this.activeDate = date;
10076         if(vd && this.el){
10077             var t = date.getTime();
10078             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10079                 this.cells.removeClass("x-date-selected");
10080                 this.cells.each(function(c){
10081                    if(c.dom.firstChild.dateValue == t){
10082                        c.addClass("x-date-selected");
10083                        setTimeout(function(){
10084                             try{c.dom.firstChild.focus();}catch(e){}
10085                        }, 50);
10086                        return false;
10087                    }
10088                 });
10089                 return;
10090             }
10091         }
10092         
10093         var days = date.getDaysInMonth();
10094         var firstOfMonth = date.getFirstDateOfMonth();
10095         var startingPos = firstOfMonth.getDay()-this.startDay;
10096
10097         if(startingPos <= this.startDay){
10098             startingPos += 7;
10099         }
10100
10101         var pm = date.add("mo", -1);
10102         var prevStart = pm.getDaysInMonth()-startingPos;
10103
10104         var cells = this.cells.elements;
10105         var textEls = this.textNodes;
10106         days += startingPos;
10107
10108         // convert everything to numbers so it's fast
10109         var day = 86400000;
10110         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10111         var today = new Date().clearTime().getTime();
10112         var sel = date.clearTime().getTime();
10113         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10114         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10115         var ddMatch = this.disabledDatesRE;
10116         var ddText = this.disabledDatesText;
10117         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10118         var ddaysText = this.disabledDaysText;
10119         var format = this.format;
10120
10121         var setCellClass = function(cal, cell){
10122             cell.title = "";
10123             var t = d.getTime();
10124             cell.firstChild.dateValue = t;
10125             if(t == today){
10126                 cell.className += " x-date-today";
10127                 cell.title = cal.todayText;
10128             }
10129             if(t == sel){
10130                 cell.className += " x-date-selected";
10131                 setTimeout(function(){
10132                     try{cell.firstChild.focus();}catch(e){}
10133                 }, 50);
10134             }
10135             // disabling
10136             if(t < min) {
10137                 cell.className = " x-date-disabled";
10138                 cell.title = cal.minText;
10139                 return;
10140             }
10141             if(t > max) {
10142                 cell.className = " x-date-disabled";
10143                 cell.title = cal.maxText;
10144                 return;
10145             }
10146             if(ddays){
10147                 if(ddays.indexOf(d.getDay()) != -1){
10148                     cell.title = ddaysText;
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152             if(ddMatch && format){
10153                 var fvalue = d.dateFormat(format);
10154                 if(ddMatch.test(fvalue)){
10155                     cell.title = ddText.replace("%0", fvalue);
10156                     cell.className = " x-date-disabled";
10157                 }
10158             }
10159         };
10160
10161         var i = 0;
10162         for(; i < startingPos; i++) {
10163             textEls[i].innerHTML = (++prevStart);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-prevday";
10166             setCellClass(this, cells[i]);
10167         }
10168         for(; i < days; i++){
10169             intDay = i - startingPos + 1;
10170             textEls[i].innerHTML = (intDay);
10171             d.setDate(d.getDate()+1);
10172             cells[i].className = "x-date-active";
10173             setCellClass(this, cells[i]);
10174         }
10175         var extraDays = 0;
10176         for(; i < 42; i++) {
10177              textEls[i].innerHTML = (++extraDays);
10178              d.setDate(d.getDate()+1);
10179              cells[i].className = "x-date-nextday";
10180              setCellClass(this, cells[i]);
10181         }
10182
10183         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10184         this.fireEvent('monthchange', this, date);
10185         
10186         if(!this.internalRender){
10187             var main = this.el.dom.firstChild;
10188             var w = main.offsetWidth;
10189             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10190             Roo.fly(main).setWidth(w);
10191             this.internalRender = true;
10192             // opera does not respect the auto grow header center column
10193             // then, after it gets a width opera refuses to recalculate
10194             // without a second pass
10195             if(Roo.isOpera && !this.secondPass){
10196                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10197                 this.secondPass = true;
10198                 this.update.defer(10, this, [date]);
10199             }
10200         }
10201         
10202         
10203     }
10204 });        /*
10205  * Based on:
10206  * Ext JS Library 1.1.1
10207  * Copyright(c) 2006-2007, Ext JS, LLC.
10208  *
10209  * Originally Released Under LGPL - original licence link has changed is not relivant.
10210  *
10211  * Fork - LGPL
10212  * <script type="text/javascript">
10213  */
10214 /**
10215  * @class Roo.TabPanel
10216  * @extends Roo.util.Observable
10217  * A lightweight tab container.
10218  * <br><br>
10219  * Usage:
10220  * <pre><code>
10221 // basic tabs 1, built from existing content
10222 var tabs = new Roo.TabPanel("tabs1");
10223 tabs.addTab("script", "View Script");
10224 tabs.addTab("markup", "View Markup");
10225 tabs.activate("script");
10226
10227 // more advanced tabs, built from javascript
10228 var jtabs = new Roo.TabPanel("jtabs");
10229 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10230
10231 // set up the UpdateManager
10232 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10233 var updater = tab2.getUpdateManager();
10234 updater.setDefaultUrl("ajax1.htm");
10235 tab2.on('activate', updater.refresh, updater, true);
10236
10237 // Use setUrl for Ajax loading
10238 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10239 tab3.setUrl("ajax2.htm", null, true);
10240
10241 // Disabled tab
10242 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10243 tab4.disable();
10244
10245 jtabs.activate("jtabs-1");
10246  * </code></pre>
10247  * @constructor
10248  * Create a new TabPanel.
10249  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10250  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10251  */
10252 Roo.TabPanel = function(container, config){
10253     /**
10254     * The container element for this TabPanel.
10255     * @type Roo.Element
10256     */
10257     this.el = Roo.get(container, true);
10258     if(config){
10259         if(typeof config == "boolean"){
10260             this.tabPosition = config ? "bottom" : "top";
10261         }else{
10262             Roo.apply(this, config);
10263         }
10264     }
10265     if(this.tabPosition == "bottom"){
10266         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10267         this.el.addClass("x-tabs-bottom");
10268     }
10269     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10270     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10271     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10272     if(Roo.isIE){
10273         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10274     }
10275     if(this.tabPosition != "bottom"){
10276         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10277          * @type Roo.Element
10278          */
10279         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10280         this.el.addClass("x-tabs-top");
10281     }
10282     this.items = [];
10283
10284     this.bodyEl.setStyle("position", "relative");
10285
10286     this.active = null;
10287     this.activateDelegate = this.activate.createDelegate(this);
10288
10289     this.addEvents({
10290         /**
10291          * @event tabchange
10292          * Fires when the active tab changes
10293          * @param {Roo.TabPanel} this
10294          * @param {Roo.TabPanelItem} activePanel The new active tab
10295          */
10296         "tabchange": true,
10297         /**
10298          * @event beforetabchange
10299          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10300          * @param {Roo.TabPanel} this
10301          * @param {Object} e Set cancel to true on this object to cancel the tab change
10302          * @param {Roo.TabPanelItem} tab The tab being changed to
10303          */
10304         "beforetabchange" : true
10305     });
10306
10307     Roo.EventManager.onWindowResize(this.onResize, this);
10308     this.cpad = this.el.getPadding("lr");
10309     this.hiddenCount = 0;
10310
10311
10312     // toolbar on the tabbar support...
10313     if (this.toolbar) {
10314         var tcfg = this.toolbar;
10315         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10316         this.toolbar = new Roo.Toolbar(tcfg);
10317         if (Roo.isSafari) {
10318             var tbl = tcfg.container.child('table', true);
10319             tbl.setAttribute('width', '100%');
10320         }
10321         
10322     }
10323    
10324
10325
10326     Roo.TabPanel.superclass.constructor.call(this);
10327 };
10328
10329 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10330     /*
10331      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10332      */
10333     tabPosition : "top",
10334     /*
10335      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10336      */
10337     currentTabWidth : 0,
10338     /*
10339      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10340      */
10341     minTabWidth : 40,
10342     /*
10343      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10344      */
10345     maxTabWidth : 250,
10346     /*
10347      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10348      */
10349     preferredTabWidth : 175,
10350     /*
10351      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10352      */
10353     resizeTabs : false,
10354     /*
10355      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10356      */
10357     monitorResize : true,
10358     /*
10359      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10360      */
10361     toolbar : false,
10362
10363     /**
10364      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10365      * @param {String} id The id of the div to use <b>or create</b>
10366      * @param {String} text The text for the tab
10367      * @param {String} content (optional) Content to put in the TabPanelItem body
10368      * @param {Boolean} closable (optional) True to create a close icon on the tab
10369      * @return {Roo.TabPanelItem} The created TabPanelItem
10370      */
10371     addTab : function(id, text, content, closable){
10372         var item = new Roo.TabPanelItem(this, id, text, closable);
10373         this.addTabItem(item);
10374         if(content){
10375             item.setContent(content);
10376         }
10377         return item;
10378     },
10379
10380     /**
10381      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10382      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10383      * @return {Roo.TabPanelItem}
10384      */
10385     getTab : function(id){
10386         return this.items[id];
10387     },
10388
10389     /**
10390      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10391      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10392      */
10393     hideTab : function(id){
10394         var t = this.items[id];
10395         if(!t.isHidden()){
10396            t.setHidden(true);
10397            this.hiddenCount++;
10398            this.autoSizeTabs();
10399         }
10400     },
10401
10402     /**
10403      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10404      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10405      */
10406     unhideTab : function(id){
10407         var t = this.items[id];
10408         if(t.isHidden()){
10409            t.setHidden(false);
10410            this.hiddenCount--;
10411            this.autoSizeTabs();
10412         }
10413     },
10414
10415     /**
10416      * Adds an existing {@link Roo.TabPanelItem}.
10417      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10418      */
10419     addTabItem : function(item){
10420         this.items[item.id] = item;
10421         this.items.push(item);
10422         if(this.resizeTabs){
10423            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10424            this.autoSizeTabs();
10425         }else{
10426             item.autoSize();
10427         }
10428     },
10429
10430     /**
10431      * Removes a {@link Roo.TabPanelItem}.
10432      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10433      */
10434     removeTab : function(id){
10435         var items = this.items;
10436         var tab = items[id];
10437         if(!tab) { return; }
10438         var index = items.indexOf(tab);
10439         if(this.active == tab && items.length > 1){
10440             var newTab = this.getNextAvailable(index);
10441             if(newTab) {
10442                 newTab.activate();
10443             }
10444         }
10445         this.stripEl.dom.removeChild(tab.pnode.dom);
10446         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10447             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10448         }
10449         items.splice(index, 1);
10450         delete this.items[tab.id];
10451         tab.fireEvent("close", tab);
10452         tab.purgeListeners();
10453         this.autoSizeTabs();
10454     },
10455
10456     getNextAvailable : function(start){
10457         var items = this.items;
10458         var index = start;
10459         // look for a next tab that will slide over to
10460         // replace the one being removed
10461         while(index < items.length){
10462             var item = items[++index];
10463             if(item && !item.isHidden()){
10464                 return item;
10465             }
10466         }
10467         // if one isn't found select the previous tab (on the left)
10468         index = start;
10469         while(index >= 0){
10470             var item = items[--index];
10471             if(item && !item.isHidden()){
10472                 return item;
10473             }
10474         }
10475         return null;
10476     },
10477
10478     /**
10479      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10480      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10481      */
10482     disableTab : function(id){
10483         var tab = this.items[id];
10484         if(tab && this.active != tab){
10485             tab.disable();
10486         }
10487     },
10488
10489     /**
10490      * Enables a {@link Roo.TabPanelItem} that is disabled.
10491      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10492      */
10493     enableTab : function(id){
10494         var tab = this.items[id];
10495         tab.enable();
10496     },
10497
10498     /**
10499      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10500      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10501      * @return {Roo.TabPanelItem} The TabPanelItem.
10502      */
10503     activate : function(id){
10504         var tab = this.items[id];
10505         if(!tab){
10506             return null;
10507         }
10508         if(tab == this.active || tab.disabled){
10509             return tab;
10510         }
10511         var e = {};
10512         this.fireEvent("beforetabchange", this, e, tab);
10513         if(e.cancel !== true && !tab.disabled){
10514             if(this.active){
10515                 this.active.hide();
10516             }
10517             this.active = this.items[id];
10518             this.active.show();
10519             this.fireEvent("tabchange", this, this.active);
10520         }
10521         return tab;
10522     },
10523
10524     /**
10525      * Gets the active {@link Roo.TabPanelItem}.
10526      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10527      */
10528     getActiveTab : function(){
10529         return this.active;
10530     },
10531
10532     /**
10533      * Updates the tab body element to fit the height of the container element
10534      * for overflow scrolling
10535      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10536      */
10537     syncHeight : function(targetHeight){
10538         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10539         var bm = this.bodyEl.getMargins();
10540         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10541         this.bodyEl.setHeight(newHeight);
10542         return newHeight;
10543     },
10544
10545     onResize : function(){
10546         if(this.monitorResize){
10547             this.autoSizeTabs();
10548         }
10549     },
10550
10551     /**
10552      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     beginUpdate : function(){
10555         this.updating = true;
10556     },
10557
10558     /**
10559      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10560      */
10561     endUpdate : function(){
10562         this.updating = false;
10563         this.autoSizeTabs();
10564     },
10565
10566     /**
10567      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10568      */
10569     autoSizeTabs : function(){
10570         var count = this.items.length;
10571         var vcount = count - this.hiddenCount;
10572         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
10573             return;
10574         }
10575         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10576         var availWidth = Math.floor(w / vcount);
10577         var b = this.stripBody;
10578         if(b.getWidth() > w){
10579             var tabs = this.items;
10580             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10581             if(availWidth < this.minTabWidth){
10582                 /*if(!this.sleft){    // incomplete scrolling code
10583                     this.createScrollButtons();
10584                 }
10585                 this.showScroll();
10586                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10587             }
10588         }else{
10589             if(this.currentTabWidth < this.preferredTabWidth){
10590                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10591             }
10592         }
10593     },
10594
10595     /**
10596      * Returns the number of tabs in this TabPanel.
10597      * @return {Number}
10598      */
10599      getCount : function(){
10600          return this.items.length;
10601      },
10602
10603     /**
10604      * Resizes all the tabs to the passed width
10605      * @param {Number} The new width
10606      */
10607     setTabWidth : function(width){
10608         this.currentTabWidth = width;
10609         for(var i = 0, len = this.items.length; i < len; i++) {
10610                 if(!this.items[i].isHidden()) {
10611                 this.items[i].setWidth(width);
10612             }
10613         }
10614     },
10615
10616     /**
10617      * Destroys this TabPanel
10618      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10619      */
10620     destroy : function(removeEl){
10621         Roo.EventManager.removeResizeListener(this.onResize, this);
10622         for(var i = 0, len = this.items.length; i < len; i++){
10623             this.items[i].purgeListeners();
10624         }
10625         if(removeEl === true){
10626             this.el.update("");
10627             this.el.remove();
10628         }
10629     }
10630 });
10631
10632 /**
10633  * @class Roo.TabPanelItem
10634  * @extends Roo.util.Observable
10635  * Represents an individual item (tab plus body) in a TabPanel.
10636  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10637  * @param {String} id The id of this TabPanelItem
10638  * @param {String} text The text for the tab of this TabPanelItem
10639  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10640  */
10641 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10642     /**
10643      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10644      * @type Roo.TabPanel
10645      */
10646     this.tabPanel = tabPanel;
10647     /**
10648      * The id for this TabPanelItem
10649      * @type String
10650      */
10651     this.id = id;
10652     /** @private */
10653     this.disabled = false;
10654     /** @private */
10655     this.text = text;
10656     /** @private */
10657     this.loaded = false;
10658     this.closable = closable;
10659
10660     /**
10661      * The body element for this TabPanelItem.
10662      * @type Roo.Element
10663      */
10664     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10665     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10666     this.bodyEl.setStyle("display", "block");
10667     this.bodyEl.setStyle("zoom", "1");
10668     this.hideAction();
10669
10670     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10671     /** @private */
10672     this.el = Roo.get(els.el, true);
10673     this.inner = Roo.get(els.inner, true);
10674     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10675     this.pnode = Roo.get(els.el.parentNode, true);
10676     this.el.on("mousedown", this.onTabMouseDown, this);
10677     this.el.on("click", this.onTabClick, this);
10678     /** @private */
10679     if(closable){
10680         var c = Roo.get(els.close, true);
10681         c.dom.title = this.closeText;
10682         c.addClassOnOver("close-over");
10683         c.on("click", this.closeClick, this);
10684      }
10685
10686     this.addEvents({
10687          /**
10688          * @event activate
10689          * Fires when this tab becomes the active tab.
10690          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10691          * @param {Roo.TabPanelItem} this
10692          */
10693         "activate": true,
10694         /**
10695          * @event beforeclose
10696          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10697          * @param {Roo.TabPanelItem} this
10698          * @param {Object} e Set cancel to true on this object to cancel the close.
10699          */
10700         "beforeclose": true,
10701         /**
10702          * @event close
10703          * Fires when this tab is closed.
10704          * @param {Roo.TabPanelItem} this
10705          */
10706          "close": true,
10707         /**
10708          * @event deactivate
10709          * Fires when this tab is no longer the active tab.
10710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10711          * @param {Roo.TabPanelItem} this
10712          */
10713          "deactivate" : true
10714     });
10715     this.hidden = false;
10716
10717     Roo.TabPanelItem.superclass.constructor.call(this);
10718 };
10719
10720 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10721     purgeListeners : function(){
10722        Roo.util.Observable.prototype.purgeListeners.call(this);
10723        this.el.removeAllListeners();
10724     },
10725     /**
10726      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10727      */
10728     show : function(){
10729         this.pnode.addClass("on");
10730         this.showAction();
10731         if(Roo.isOpera){
10732             this.tabPanel.stripWrap.repaint();
10733         }
10734         this.fireEvent("activate", this.tabPanel, this);
10735     },
10736
10737     /**
10738      * Returns true if this tab is the active tab.
10739      * @return {Boolean}
10740      */
10741     isActive : function(){
10742         return this.tabPanel.getActiveTab() == this;
10743     },
10744
10745     /**
10746      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10747      */
10748     hide : function(){
10749         this.pnode.removeClass("on");
10750         this.hideAction();
10751         this.fireEvent("deactivate", this.tabPanel, this);
10752     },
10753
10754     hideAction : function(){
10755         this.bodyEl.hide();
10756         this.bodyEl.setStyle("position", "absolute");
10757         this.bodyEl.setLeft("-20000px");
10758         this.bodyEl.setTop("-20000px");
10759     },
10760
10761     showAction : function(){
10762         this.bodyEl.setStyle("position", "relative");
10763         this.bodyEl.setTop("");
10764         this.bodyEl.setLeft("");
10765         this.bodyEl.show();
10766     },
10767
10768     /**
10769      * Set the tooltip for the tab.
10770      * @param {String} tooltip The tab's tooltip
10771      */
10772     setTooltip : function(text){
10773         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10774             this.textEl.dom.qtip = text;
10775             this.textEl.dom.removeAttribute('title');
10776         }else{
10777             this.textEl.dom.title = text;
10778         }
10779     },
10780
10781     onTabClick : function(e){
10782         e.preventDefault();
10783         this.tabPanel.activate(this.id);
10784     },
10785
10786     onTabMouseDown : function(e){
10787         e.preventDefault();
10788         this.tabPanel.activate(this.id);
10789     },
10790
10791     getWidth : function(){
10792         return this.inner.getWidth();
10793     },
10794
10795     setWidth : function(width){
10796         var iwidth = width - this.pnode.getPadding("lr");
10797         this.inner.setWidth(iwidth);
10798         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10799         this.pnode.setWidth(width);
10800     },
10801
10802     /**
10803      * Show or hide the tab
10804      * @param {Boolean} hidden True to hide or false to show.
10805      */
10806     setHidden : function(hidden){
10807         this.hidden = hidden;
10808         this.pnode.setStyle("display", hidden ? "none" : "");
10809     },
10810
10811     /**
10812      * Returns true if this tab is "hidden"
10813      * @return {Boolean}
10814      */
10815     isHidden : function(){
10816         return this.hidden;
10817     },
10818
10819     /**
10820      * Returns the text for this tab
10821      * @return {String}
10822      */
10823     getText : function(){
10824         return this.text;
10825     },
10826
10827     autoSize : function(){
10828         //this.el.beginMeasure();
10829         this.textEl.setWidth(1);
10830         /*
10831          *  #2804 [new] Tabs in Roojs
10832          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10833          */
10834         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10835         //this.el.endMeasure();
10836     },
10837
10838     /**
10839      * Sets the text for the tab (Note: this also sets the tooltip text)
10840      * @param {String} text The tab's text and tooltip
10841      */
10842     setText : function(text){
10843         this.text = text;
10844         this.textEl.update(text);
10845         this.setTooltip(text);
10846         if(!this.tabPanel.resizeTabs){
10847             this.autoSize();
10848         }
10849     },
10850     /**
10851      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10852      */
10853     activate : function(){
10854         this.tabPanel.activate(this.id);
10855     },
10856
10857     /**
10858      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10859      */
10860     disable : function(){
10861         if(this.tabPanel.active != this){
10862             this.disabled = true;
10863             this.pnode.addClass("disabled");
10864         }
10865     },
10866
10867     /**
10868      * Enables this TabPanelItem if it was previously disabled.
10869      */
10870     enable : function(){
10871         this.disabled = false;
10872         this.pnode.removeClass("disabled");
10873     },
10874
10875     /**
10876      * Sets the content for this TabPanelItem.
10877      * @param {String} content The content
10878      * @param {Boolean} loadScripts true to look for and load scripts
10879      */
10880     setContent : function(content, loadScripts){
10881         this.bodyEl.update(content, loadScripts);
10882     },
10883
10884     /**
10885      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     getUpdateManager : function(){
10889         return this.bodyEl.getUpdateManager();
10890     },
10891
10892     /**
10893      * Set a URL to be used to load the content for this TabPanelItem.
10894      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10895      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
10896      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
10897      * @return {Roo.UpdateManager} The UpdateManager
10898      */
10899     setUrl : function(url, params, loadOnce){
10900         if(this.refreshDelegate){
10901             this.un('activate', this.refreshDelegate);
10902         }
10903         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10904         this.on("activate", this.refreshDelegate);
10905         return this.bodyEl.getUpdateManager();
10906     },
10907
10908     /** @private */
10909     _handleRefresh : function(url, params, loadOnce){
10910         if(!loadOnce || !this.loaded){
10911             var updater = this.bodyEl.getUpdateManager();
10912             updater.update(url, params, this._setLoaded.createDelegate(this));
10913         }
10914     },
10915
10916     /**
10917      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10918      *   Will fail silently if the setUrl method has not been called.
10919      *   This does not activate the panel, just updates its content.
10920      */
10921     refresh : function(){
10922         if(this.refreshDelegate){
10923            this.loaded = false;
10924            this.refreshDelegate();
10925         }
10926     },
10927
10928     /** @private */
10929     _setLoaded : function(){
10930         this.loaded = true;
10931     },
10932
10933     /** @private */
10934     closeClick : function(e){
10935         var o = {};
10936         e.stopEvent();
10937         this.fireEvent("beforeclose", this, o);
10938         if(o.cancel !== true){
10939             this.tabPanel.removeTab(this.id);
10940         }
10941     },
10942     /**
10943      * The text displayed in the tooltip for the close icon.
10944      * @type String
10945      */
10946     closeText : "Close this tab"
10947 });
10948
10949 /** @private */
10950 Roo.TabPanel.prototype.createStrip = function(container){
10951     var strip = document.createElement("div");
10952     strip.className = "x-tabs-wrap";
10953     container.appendChild(strip);
10954     return strip;
10955 };
10956 /** @private */
10957 Roo.TabPanel.prototype.createStripList = function(strip){
10958     // div wrapper for retard IE
10959     // returns the "tr" element.
10960     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10961         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10962         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10963     return strip.firstChild.firstChild.firstChild.firstChild;
10964 };
10965 /** @private */
10966 Roo.TabPanel.prototype.createBody = function(container){
10967     var body = document.createElement("div");
10968     Roo.id(body, "tab-body");
10969     Roo.fly(body).addClass("x-tabs-body");
10970     container.appendChild(body);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10975     var body = Roo.getDom(id);
10976     if(!body){
10977         body = document.createElement("div");
10978         body.id = id;
10979     }
10980     Roo.fly(body).addClass("x-tabs-item-body");
10981     bodyEl.insertBefore(body, bodyEl.firstChild);
10982     return body;
10983 };
10984 /** @private */
10985 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10986     var td = document.createElement("td");
10987     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10988     //stripEl.appendChild(td);
10989     if(closable){
10990         td.className = "x-tabs-closable";
10991         if(!this.closeTpl){
10992             this.closeTpl = new Roo.Template(
10993                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10994                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10995                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10996             );
10997         }
10998         var el = this.closeTpl.overwrite(td, {"text": text});
10999         var close = el.getElementsByTagName("div")[0];
11000         var inner = el.getElementsByTagName("em")[0];
11001         return {"el": el, "close": close, "inner": inner};
11002     } else {
11003         if(!this.tabTpl){
11004             this.tabTpl = new Roo.Template(
11005                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11006                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11007             );
11008         }
11009         var el = this.tabTpl.overwrite(td, {"text": text});
11010         var inner = el.getElementsByTagName("em")[0];
11011         return {"el": el, "inner": inner};
11012     }
11013 };/*
11014  * Based on:
11015  * Ext JS Library 1.1.1
11016  * Copyright(c) 2006-2007, Ext JS, LLC.
11017  *
11018  * Originally Released Under LGPL - original licence link has changed is not relivant.
11019  *
11020  * Fork - LGPL
11021  * <script type="text/javascript">
11022  */
11023
11024 /**
11025  * @class Roo.Button
11026  * @extends Roo.util.Observable
11027  * Simple Button class
11028  * @cfg {String} text The button text
11029  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11030  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11031  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11032  * @cfg {Object} scope The scope of the handler
11033  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11034  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11035  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11036  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11037  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11038  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11039    applies if enableToggle = true)
11040  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11041  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11042   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11043  * @constructor
11044  * Create a new button
11045  * @param {Object} config The config object
11046  */
11047 Roo.Button = function(renderTo, config)
11048 {
11049     if (!config) {
11050         config = renderTo;
11051         renderTo = config.renderTo || false;
11052     }
11053     
11054     Roo.apply(this, config);
11055     this.addEvents({
11056         /**
11057              * @event click
11058              * Fires when this button is clicked
11059              * @param {Button} this
11060              * @param {EventObject} e The click event
11061              */
11062             "click" : true,
11063         /**
11064              * @event toggle
11065              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11066              * @param {Button} this
11067              * @param {Boolean} pressed
11068              */
11069             "toggle" : true,
11070         /**
11071              * @event mouseover
11072              * Fires when the mouse hovers over the button
11073              * @param {Button} this
11074              * @param {Event} e The event object
11075              */
11076         'mouseover' : true,
11077         /**
11078              * @event mouseout
11079              * Fires when the mouse exits the button
11080              * @param {Button} this
11081              * @param {Event} e The event object
11082              */
11083         'mouseout': true,
11084          /**
11085              * @event render
11086              * Fires when the button is rendered
11087              * @param {Button} this
11088              */
11089         'render': true
11090     });
11091     if(this.menu){
11092         this.menu = Roo.menu.MenuMgr.get(this.menu);
11093     }
11094     // register listeners first!!  - so render can be captured..
11095     Roo.util.Observable.call(this);
11096     if(renderTo){
11097         this.render(renderTo);
11098     }
11099     
11100   
11101 };
11102
11103 Roo.extend(Roo.Button, Roo.util.Observable, {
11104     /**
11105      * 
11106      */
11107     
11108     /**
11109      * Read-only. True if this button is hidden
11110      * @type Boolean
11111      */
11112     hidden : false,
11113     /**
11114      * Read-only. True if this button is disabled
11115      * @type Boolean
11116      */
11117     disabled : false,
11118     /**
11119      * Read-only. True if this button is pressed (only if enableToggle = true)
11120      * @type Boolean
11121      */
11122     pressed : false,
11123
11124     /**
11125      * @cfg {Number} tabIndex 
11126      * The DOM tabIndex for this button (defaults to undefined)
11127      */
11128     tabIndex : undefined,
11129
11130     /**
11131      * @cfg {Boolean} enableToggle
11132      * True to enable pressed/not pressed toggling (defaults to false)
11133      */
11134     enableToggle: false,
11135     /**
11136      * @cfg {Mixed} menu
11137      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11138      */
11139     menu : undefined,
11140     /**
11141      * @cfg {String} menuAlign
11142      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11143      */
11144     menuAlign : "tl-bl?",
11145
11146     /**
11147      * @cfg {String} iconCls
11148      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11149      */
11150     iconCls : undefined,
11151     /**
11152      * @cfg {String} type
11153      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11154      */
11155     type : 'button',
11156
11157     // private
11158     menuClassTarget: 'tr',
11159
11160     /**
11161      * @cfg {String} clickEvent
11162      * The type of event to map to the button's event handler (defaults to 'click')
11163      */
11164     clickEvent : 'click',
11165
11166     /**
11167      * @cfg {Boolean} handleMouseEvents
11168      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11169      */
11170     handleMouseEvents : true,
11171
11172     /**
11173      * @cfg {String} tooltipType
11174      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11175      */
11176     tooltipType : 'qtip',
11177
11178     /**
11179      * @cfg {String} cls
11180      * A CSS class to apply to the button's main element.
11181      */
11182     
11183     /**
11184      * @cfg {Roo.Template} template (Optional)
11185      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11186      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11187      * require code modifications if required elements (e.g. a button) aren't present.
11188      */
11189
11190     // private
11191     render : function(renderTo){
11192         var btn;
11193         if(this.hideParent){
11194             this.parentEl = Roo.get(renderTo);
11195         }
11196         if(!this.dhconfig){
11197             if(!this.template){
11198                 if(!Roo.Button.buttonTemplate){
11199                     // hideous table template
11200                     Roo.Button.buttonTemplate = new Roo.Template(
11201                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11202                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11203                         "</tr></tbody></table>");
11204                 }
11205                 this.template = Roo.Button.buttonTemplate;
11206             }
11207             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11208             var btnEl = btn.child("button:first");
11209             btnEl.on('focus', this.onFocus, this);
11210             btnEl.on('blur', this.onBlur, this);
11211             if(this.cls){
11212                 btn.addClass(this.cls);
11213             }
11214             if(this.icon){
11215                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11216             }
11217             if(this.iconCls){
11218                 btnEl.addClass(this.iconCls);
11219                 if(!this.cls){
11220                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11221                 }
11222             }
11223             if(this.tabIndex !== undefined){
11224                 btnEl.dom.tabIndex = this.tabIndex;
11225             }
11226             if(this.tooltip){
11227                 if(typeof this.tooltip == 'object'){
11228                     Roo.QuickTips.tips(Roo.apply({
11229                           target: btnEl.id
11230                     }, this.tooltip));
11231                 } else {
11232                     btnEl.dom[this.tooltipType] = this.tooltip;
11233                 }
11234             }
11235         }else{
11236             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11237         }
11238         this.el = btn;
11239         if(this.id){
11240             this.el.dom.id = this.el.id = this.id;
11241         }
11242         if(this.menu){
11243             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11244             this.menu.on("show", this.onMenuShow, this);
11245             this.menu.on("hide", this.onMenuHide, this);
11246         }
11247         btn.addClass("x-btn");
11248         if(Roo.isIE && !Roo.isIE7){
11249             this.autoWidth.defer(1, this);
11250         }else{
11251             this.autoWidth();
11252         }
11253         if(this.handleMouseEvents){
11254             btn.on("mouseover", this.onMouseOver, this);
11255             btn.on("mouseout", this.onMouseOut, this);
11256             btn.on("mousedown", this.onMouseDown, this);
11257         }
11258         btn.on(this.clickEvent, this.onClick, this);
11259         //btn.on("mouseup", this.onMouseUp, this);
11260         if(this.hidden){
11261             this.hide();
11262         }
11263         if(this.disabled){
11264             this.disable();
11265         }
11266         Roo.ButtonToggleMgr.register(this);
11267         if(this.pressed){
11268             this.el.addClass("x-btn-pressed");
11269         }
11270         if(this.repeat){
11271             var repeater = new Roo.util.ClickRepeater(btn,
11272                 typeof this.repeat == "object" ? this.repeat : {}
11273             );
11274             repeater.on("click", this.onClick,  this);
11275         }
11276         
11277         this.fireEvent('render', this);
11278         
11279     },
11280     /**
11281      * Returns the button's underlying element
11282      * @return {Roo.Element} The element
11283      */
11284     getEl : function(){
11285         return this.el;  
11286     },
11287     
11288     /**
11289      * Destroys this Button and removes any listeners.
11290      */
11291     destroy : function(){
11292         Roo.ButtonToggleMgr.unregister(this);
11293         this.el.removeAllListeners();
11294         this.purgeListeners();
11295         this.el.remove();
11296     },
11297
11298     // private
11299     autoWidth : function(){
11300         if(this.el){
11301             this.el.setWidth("auto");
11302             if(Roo.isIE7 && Roo.isStrict){
11303                 var ib = this.el.child('button');
11304                 if(ib && ib.getWidth() > 20){
11305                     ib.clip();
11306                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11307                 }
11308             }
11309             if(this.minWidth){
11310                 if(this.hidden){
11311                     this.el.beginMeasure();
11312                 }
11313                 if(this.el.getWidth() < this.minWidth){
11314                     this.el.setWidth(this.minWidth);
11315                 }
11316                 if(this.hidden){
11317                     this.el.endMeasure();
11318                 }
11319             }
11320         }
11321     },
11322
11323     /**
11324      * Assigns this button's click handler
11325      * @param {Function} handler The function to call when the button is clicked
11326      * @param {Object} scope (optional) Scope for the function passed in
11327      */
11328     setHandler : function(handler, scope){
11329         this.handler = handler;
11330         this.scope = scope;  
11331     },
11332     
11333     /**
11334      * Sets this button's text
11335      * @param {String} text The button text
11336      */
11337     setText : function(text){
11338         this.text = text;
11339         if(this.el){
11340             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11341         }
11342         this.autoWidth();
11343     },
11344     
11345     /**
11346      * Gets the text for this button
11347      * @return {String} The button text
11348      */
11349     getText : function(){
11350         return this.text;  
11351     },
11352     
11353     /**
11354      * Show this button
11355      */
11356     show: function(){
11357         this.hidden = false;
11358         if(this.el){
11359             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11360         }
11361     },
11362     
11363     /**
11364      * Hide this button
11365      */
11366     hide: function(){
11367         this.hidden = true;
11368         if(this.el){
11369             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11370         }
11371     },
11372     
11373     /**
11374      * Convenience function for boolean show/hide
11375      * @param {Boolean} visible True to show, false to hide
11376      */
11377     setVisible: function(visible){
11378         if(visible) {
11379             this.show();
11380         }else{
11381             this.hide();
11382         }
11383     },
11384     
11385     /**
11386      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11387      * @param {Boolean} state (optional) Force a particular state
11388      */
11389     toggle : function(state){
11390         state = state === undefined ? !this.pressed : state;
11391         if(state != this.pressed){
11392             if(state){
11393                 this.el.addClass("x-btn-pressed");
11394                 this.pressed = true;
11395                 this.fireEvent("toggle", this, true);
11396             }else{
11397                 this.el.removeClass("x-btn-pressed");
11398                 this.pressed = false;
11399                 this.fireEvent("toggle", this, false);
11400             }
11401             if(this.toggleHandler){
11402                 this.toggleHandler.call(this.scope || this, this, state);
11403             }
11404         }
11405     },
11406     
11407     /**
11408      * Focus the button
11409      */
11410     focus : function(){
11411         this.el.child('button:first').focus();
11412     },
11413     
11414     /**
11415      * Disable this button
11416      */
11417     disable : function(){
11418         if(this.el){
11419             this.el.addClass("x-btn-disabled");
11420         }
11421         this.disabled = true;
11422     },
11423     
11424     /**
11425      * Enable this button
11426      */
11427     enable : function(){
11428         if(this.el){
11429             this.el.removeClass("x-btn-disabled");
11430         }
11431         this.disabled = false;
11432     },
11433
11434     /**
11435      * Convenience function for boolean enable/disable
11436      * @param {Boolean} enabled True to enable, false to disable
11437      */
11438     setDisabled : function(v){
11439         this[v !== true ? "enable" : "disable"]();
11440     },
11441
11442     // private
11443     onClick : function(e)
11444     {
11445         if(e){
11446             e.preventDefault();
11447         }
11448         if(e.button != 0){
11449             return;
11450         }
11451         if(!this.disabled){
11452             if(this.enableToggle){
11453                 this.toggle();
11454             }
11455             if(this.menu && !this.menu.isVisible()){
11456                 this.menu.show(this.el, this.menuAlign);
11457             }
11458             this.fireEvent("click", this, e);
11459             if(this.handler){
11460                 this.el.removeClass("x-btn-over");
11461                 this.handler.call(this.scope || this, this, e);
11462             }
11463         }
11464     },
11465     // private
11466     onMouseOver : function(e){
11467         if(!this.disabled){
11468             this.el.addClass("x-btn-over");
11469             this.fireEvent('mouseover', this, e);
11470         }
11471     },
11472     // private
11473     onMouseOut : function(e){
11474         if(!e.within(this.el,  true)){
11475             this.el.removeClass("x-btn-over");
11476             this.fireEvent('mouseout', this, e);
11477         }
11478     },
11479     // private
11480     onFocus : function(e){
11481         if(!this.disabled){
11482             this.el.addClass("x-btn-focus");
11483         }
11484     },
11485     // private
11486     onBlur : function(e){
11487         this.el.removeClass("x-btn-focus");
11488     },
11489     // private
11490     onMouseDown : function(e){
11491         if(!this.disabled && e.button == 0){
11492             this.el.addClass("x-btn-click");
11493             Roo.get(document).on('mouseup', this.onMouseUp, this);
11494         }
11495     },
11496     // private
11497     onMouseUp : function(e){
11498         if(e.button == 0){
11499             this.el.removeClass("x-btn-click");
11500             Roo.get(document).un('mouseup', this.onMouseUp, this);
11501         }
11502     },
11503     // private
11504     onMenuShow : function(e){
11505         this.el.addClass("x-btn-menu-active");
11506     },
11507     // private
11508     onMenuHide : function(e){
11509         this.el.removeClass("x-btn-menu-active");
11510     }   
11511 });
11512
11513 // Private utility class used by Button
11514 Roo.ButtonToggleMgr = function(){
11515    var groups = {};
11516    
11517    function toggleGroup(btn, state){
11518        if(state){
11519            var g = groups[btn.toggleGroup];
11520            for(var i = 0, l = g.length; i < l; i++){
11521                if(g[i] != btn){
11522                    g[i].toggle(false);
11523                }
11524            }
11525        }
11526    }
11527    
11528    return {
11529        register : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(!g){
11535                g = groups[btn.toggleGroup] = [];
11536            }
11537            g.push(btn);
11538            btn.on("toggle", toggleGroup);
11539        },
11540        
11541        unregister : function(btn){
11542            if(!btn.toggleGroup){
11543                return;
11544            }
11545            var g = groups[btn.toggleGroup];
11546            if(g){
11547                g.remove(btn);
11548                btn.un("toggle", toggleGroup);
11549            }
11550        }
11551    };
11552 }();/*
11553  * Based on:
11554  * Ext JS Library 1.1.1
11555  * Copyright(c) 2006-2007, Ext JS, LLC.
11556  *
11557  * Originally Released Under LGPL - original licence link has changed is not relivant.
11558  *
11559  * Fork - LGPL
11560  * <script type="text/javascript">
11561  */
11562  
11563 /**
11564  * @class Roo.SplitButton
11565  * @extends Roo.Button
11566  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11567  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11568  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11569  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11570  * @cfg {String} arrowTooltip The title attribute of the arrow
11571  * @constructor
11572  * Create a new menu button
11573  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11574  * @param {Object} config The config object
11575  */
11576 Roo.SplitButton = function(renderTo, config){
11577     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11578     /**
11579      * @event arrowclick
11580      * Fires when this button's arrow is clicked
11581      * @param {SplitButton} this
11582      * @param {EventObject} e The click event
11583      */
11584     this.addEvents({"arrowclick":true});
11585 };
11586
11587 Roo.extend(Roo.SplitButton, Roo.Button, {
11588     render : function(renderTo){
11589         // this is one sweet looking template!
11590         var tpl = new Roo.Template(
11591             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11592             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11593             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
11594             "</tbody></table></td><td>",
11595             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11596             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
11597             "</tbody></table></td></tr></table>"
11598         );
11599         var btn = tpl.append(renderTo, [this.text, this.type], true);
11600         var btnEl = btn.child("button");
11601         if(this.cls){
11602             btn.addClass(this.cls);
11603         }
11604         if(this.icon){
11605             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11606         }
11607         if(this.iconCls){
11608             btnEl.addClass(this.iconCls);
11609             if(!this.cls){
11610                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11611             }
11612         }
11613         this.el = btn;
11614         if(this.handleMouseEvents){
11615             btn.on("mouseover", this.onMouseOver, this);
11616             btn.on("mouseout", this.onMouseOut, this);
11617             btn.on("mousedown", this.onMouseDown, this);
11618             btn.on("mouseup", this.onMouseUp, this);
11619         }
11620         btn.on(this.clickEvent, this.onClick, this);
11621         if(this.tooltip){
11622             if(typeof this.tooltip == 'object'){
11623                 Roo.QuickTips.tips(Roo.apply({
11624                       target: btnEl.id
11625                 }, this.tooltip));
11626             } else {
11627                 btnEl.dom[this.tooltipType] = this.tooltip;
11628             }
11629         }
11630         if(this.arrowTooltip){
11631             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11632         }
11633         if(this.hidden){
11634             this.hide();
11635         }
11636         if(this.disabled){
11637             this.disable();
11638         }
11639         if(this.pressed){
11640             this.el.addClass("x-btn-pressed");
11641         }
11642         if(Roo.isIE && !Roo.isIE7){
11643             this.autoWidth.defer(1, this);
11644         }else{
11645             this.autoWidth();
11646         }
11647         if(this.menu){
11648             this.menu.on("show", this.onMenuShow, this);
11649             this.menu.on("hide", this.onMenuHide, this);
11650         }
11651         this.fireEvent('render', this);
11652     },
11653
11654     // private
11655     autoWidth : function(){
11656         if(this.el){
11657             var tbl = this.el.child("table:first");
11658             var tbl2 = this.el.child("table:last");
11659             this.el.setWidth("auto");
11660             tbl.setWidth("auto");
11661             if(Roo.isIE7 && Roo.isStrict){
11662                 var ib = this.el.child('button:first');
11663                 if(ib && ib.getWidth() > 20){
11664                     ib.clip();
11665                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11666                 }
11667             }
11668             if(this.minWidth){
11669                 if(this.hidden){
11670                     this.el.beginMeasure();
11671                 }
11672                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11673                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11674                 }
11675                 if(this.hidden){
11676                     this.el.endMeasure();
11677                 }
11678             }
11679             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11680         } 
11681     },
11682     /**
11683      * Sets this button's click handler
11684      * @param {Function} handler The function to call when the button is clicked
11685      * @param {Object} scope (optional) Scope for the function passed above
11686      */
11687     setHandler : function(handler, scope){
11688         this.handler = handler;
11689         this.scope = scope;  
11690     },
11691     
11692     /**
11693      * Sets this button's arrow click handler
11694      * @param {Function} handler The function to call when the arrow is clicked
11695      * @param {Object} scope (optional) Scope for the function passed above
11696      */
11697     setArrowHandler : function(handler, scope){
11698         this.arrowHandler = handler;
11699         this.scope = scope;  
11700     },
11701     
11702     /**
11703      * Focus the button
11704      */
11705     focus : function(){
11706         if(this.el){
11707             this.el.child("button:first").focus();
11708         }
11709     },
11710
11711     // private
11712     onClick : function(e){
11713         e.preventDefault();
11714         if(!this.disabled){
11715             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11716                 if(this.menu && !this.menu.isVisible()){
11717                     this.menu.show(this.el, this.menuAlign);
11718                 }
11719                 this.fireEvent("arrowclick", this, e);
11720                 if(this.arrowHandler){
11721                     this.arrowHandler.call(this.scope || this, this, e);
11722                 }
11723             }else{
11724                 this.fireEvent("click", this, e);
11725                 if(this.handler){
11726                     this.handler.call(this.scope || this, this, e);
11727                 }
11728             }
11729         }
11730     },
11731     // private
11732     onMouseDown : function(e){
11733         if(!this.disabled){
11734             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11735         }
11736     },
11737     // private
11738     onMouseUp : function(e){
11739         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11740     }   
11741 });
11742
11743
11744 // backwards compat
11745 Roo.MenuButton = Roo.SplitButton;/*
11746  * Based on:
11747  * Ext JS Library 1.1.1
11748  * Copyright(c) 2006-2007, Ext JS, LLC.
11749  *
11750  * Originally Released Under LGPL - original licence link has changed is not relivant.
11751  *
11752  * Fork - LGPL
11753  * <script type="text/javascript">
11754  */
11755
11756 /**
11757  * @class Roo.Toolbar
11758  * Basic Toolbar class.
11759  * @constructor
11760  * Creates a new Toolbar
11761  * @param {Object} container The config object
11762  */ 
11763 Roo.Toolbar = function(container, buttons, config)
11764 {
11765     /// old consturctor format still supported..
11766     if(container instanceof Array){ // omit the container for later rendering
11767         buttons = container;
11768         config = buttons;
11769         container = null;
11770     }
11771     if (typeof(container) == 'object' && container.xtype) {
11772         config = container;
11773         container = config.container;
11774         buttons = config.buttons || []; // not really - use items!!
11775     }
11776     var xitems = [];
11777     if (config && config.items) {
11778         xitems = config.items;
11779         delete config.items;
11780     }
11781     Roo.apply(this, config);
11782     this.buttons = buttons;
11783     
11784     if(container){
11785         this.render(container);
11786     }
11787     this.xitems = xitems;
11788     Roo.each(xitems, function(b) {
11789         this.add(b);
11790     }, this);
11791     
11792 };
11793
11794 Roo.Toolbar.prototype = {
11795     /**
11796      * @cfg {Array} items
11797      * array of button configs or elements to add (will be converted to a MixedCollection)
11798      */
11799     
11800     /**
11801      * @cfg {String/HTMLElement/Element} container
11802      * The id or element that will contain the toolbar
11803      */
11804     // private
11805     render : function(ct){
11806         this.el = Roo.get(ct);
11807         if(this.cls){
11808             this.el.addClass(this.cls);
11809         }
11810         // using a table allows for vertical alignment
11811         // 100% width is needed by Safari...
11812         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11813         this.tr = this.el.child("tr", true);
11814         var autoId = 0;
11815         this.items = new Roo.util.MixedCollection(false, function(o){
11816             return o.id || ("item" + (++autoId));
11817         });
11818         if(this.buttons){
11819             this.add.apply(this, this.buttons);
11820             delete this.buttons;
11821         }
11822     },
11823
11824     /**
11825      * Adds element(s) to the toolbar -- this function takes a variable number of 
11826      * arguments of mixed type and adds them to the toolbar.
11827      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11828      * <ul>
11829      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11830      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11831      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11832      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11833      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11834      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11835      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11836      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11837      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11838      * </ul>
11839      * @param {Mixed} arg2
11840      * @param {Mixed} etc.
11841      */
11842     add : function(){
11843         var a = arguments, l = a.length;
11844         for(var i = 0; i < l; i++){
11845             this._add(a[i]);
11846         }
11847     },
11848     // private..
11849     _add : function(el) {
11850         
11851         if (el.xtype) {
11852             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11853         }
11854         
11855         if (el.applyTo){ // some kind of form field
11856             return this.addField(el);
11857         } 
11858         if (el.render){ // some kind of Toolbar.Item
11859             return this.addItem(el);
11860         }
11861         if (typeof el == "string"){ // string
11862             if(el == "separator" || el == "-"){
11863                 return this.addSeparator();
11864             }
11865             if (el == " "){
11866                 return this.addSpacer();
11867             }
11868             if(el == "->"){
11869                 return this.addFill();
11870             }
11871             return this.addText(el);
11872             
11873         }
11874         if(el.tagName){ // element
11875             return this.addElement(el);
11876         }
11877         if(typeof el == "object"){ // must be button config?
11878             return this.addButton(el);
11879         }
11880         // and now what?!?!
11881         return false;
11882         
11883     },
11884     
11885     /**
11886      * Add an Xtype element
11887      * @param {Object} xtype Xtype Object
11888      * @return {Object} created Object
11889      */
11890     addxtype : function(e){
11891         return this.add(e);  
11892     },
11893     
11894     /**
11895      * Returns the Element for this toolbar.
11896      * @return {Roo.Element}
11897      */
11898     getEl : function(){
11899         return this.el;  
11900     },
11901     
11902     /**
11903      * Adds a separator
11904      * @return {Roo.Toolbar.Item} The separator item
11905      */
11906     addSeparator : function(){
11907         return this.addItem(new Roo.Toolbar.Separator());
11908     },
11909
11910     /**
11911      * Adds a spacer element
11912      * @return {Roo.Toolbar.Spacer} The spacer item
11913      */
11914     addSpacer : function(){
11915         return this.addItem(new Roo.Toolbar.Spacer());
11916     },
11917
11918     /**
11919      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11920      * @return {Roo.Toolbar.Fill} The fill item
11921      */
11922     addFill : function(){
11923         return this.addItem(new Roo.Toolbar.Fill());
11924     },
11925
11926     /**
11927      * Adds any standard HTML element to the toolbar
11928      * @param {String/HTMLElement/Element} el The element or id of the element to add
11929      * @return {Roo.Toolbar.Item} The element's item
11930      */
11931     addElement : function(el){
11932         return this.addItem(new Roo.Toolbar.Item(el));
11933     },
11934     /**
11935      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11936      * @type Roo.util.MixedCollection  
11937      */
11938     items : false,
11939      
11940     /**
11941      * Adds any Toolbar.Item or subclass
11942      * @param {Roo.Toolbar.Item} item
11943      * @return {Roo.Toolbar.Item} The item
11944      */
11945     addItem : function(item){
11946         var td = this.nextBlock();
11947         item.render(td);
11948         this.items.add(item);
11949         return item;
11950     },
11951     
11952     /**
11953      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11954      * @param {Object/Array} config A button config or array of configs
11955      * @return {Roo.Toolbar.Button/Array}
11956      */
11957     addButton : function(config){
11958         if(config instanceof Array){
11959             var buttons = [];
11960             for(var i = 0, len = config.length; i < len; i++) {
11961                 buttons.push(this.addButton(config[i]));
11962             }
11963             return buttons;
11964         }
11965         var b = config;
11966         if(!(config instanceof Roo.Toolbar.Button)){
11967             b = config.split ?
11968                 new Roo.Toolbar.SplitButton(config) :
11969                 new Roo.Toolbar.Button(config);
11970         }
11971         var td = this.nextBlock();
11972         b.render(td);
11973         this.items.add(b);
11974         return b;
11975     },
11976     
11977     /**
11978      * Adds text to the toolbar
11979      * @param {String} text The text to add
11980      * @return {Roo.Toolbar.Item} The element's item
11981      */
11982     addText : function(text){
11983         return this.addItem(new Roo.Toolbar.TextItem(text));
11984     },
11985     
11986     /**
11987      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11988      * @param {Number} index The index where the item is to be inserted
11989      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11990      * @return {Roo.Toolbar.Button/Item}
11991      */
11992     insertButton : function(index, item){
11993         if(item instanceof Array){
11994             var buttons = [];
11995             for(var i = 0, len = item.length; i < len; i++) {
11996                buttons.push(this.insertButton(index + i, item[i]));
11997             }
11998             return buttons;
11999         }
12000         if (!(item instanceof Roo.Toolbar.Button)){
12001            item = new Roo.Toolbar.Button(item);
12002         }
12003         var td = document.createElement("td");
12004         this.tr.insertBefore(td, this.tr.childNodes[index]);
12005         item.render(td);
12006         this.items.insert(index, item);
12007         return item;
12008     },
12009     
12010     /**
12011      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12012      * @param {Object} config
12013      * @return {Roo.Toolbar.Item} The element's item
12014      */
12015     addDom : function(config, returnEl){
12016         var td = this.nextBlock();
12017         Roo.DomHelper.overwrite(td, config);
12018         var ti = new Roo.Toolbar.Item(td.firstChild);
12019         ti.render(td);
12020         this.items.add(ti);
12021         return ti;
12022     },
12023
12024     /**
12025      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12026      * @type Roo.util.MixedCollection  
12027      */
12028     fields : false,
12029     
12030     /**
12031      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12032      * Note: the field should not have been rendered yet. For a field that has already been
12033      * rendered, use {@link #addElement}.
12034      * @param {Roo.form.Field} field
12035      * @return {Roo.ToolbarItem}
12036      */
12037      
12038       
12039     addField : function(field) {
12040         if (!this.fields) {
12041             var autoId = 0;
12042             this.fields = new Roo.util.MixedCollection(false, function(o){
12043                 return o.id || ("item" + (++autoId));
12044             });
12045
12046         }
12047         
12048         var td = this.nextBlock();
12049         field.render(td);
12050         var ti = new Roo.Toolbar.Item(td.firstChild);
12051         ti.render(td);
12052         this.items.add(ti);
12053         this.fields.add(field);
12054         return ti;
12055     },
12056     /**
12057      * Hide the toolbar
12058      * @method hide
12059      */
12060      
12061       
12062     hide : function()
12063     {
12064         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12065         this.el.child('div').hide();
12066     },
12067     /**
12068      * Show the toolbar
12069      * @method show
12070      */
12071     show : function()
12072     {
12073         this.el.child('div').show();
12074     },
12075       
12076     // private
12077     nextBlock : function(){
12078         var td = document.createElement("td");
12079         this.tr.appendChild(td);
12080         return td;
12081     },
12082
12083     // private
12084     destroy : function(){
12085         if(this.items){ // rendered?
12086             Roo.destroy.apply(Roo, this.items.items);
12087         }
12088         if(this.fields){ // rendered?
12089             Roo.destroy.apply(Roo, this.fields.items);
12090         }
12091         Roo.Element.uncache(this.el, this.tr);
12092     }
12093 };
12094
12095 /**
12096  * @class Roo.Toolbar.Item
12097  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12098  * @constructor
12099  * Creates a new Item
12100  * @param {HTMLElement} el 
12101  */
12102 Roo.Toolbar.Item = function(el){
12103     var cfg = {};
12104     if (typeof (el.xtype) != 'undefined') {
12105         cfg = el;
12106         el = cfg.el;
12107     }
12108     
12109     this.el = Roo.getDom(el);
12110     this.id = Roo.id(this.el);
12111     this.hidden = false;
12112     
12113     this.addEvents({
12114          /**
12115              * @event render
12116              * Fires when the button is rendered
12117              * @param {Button} this
12118              */
12119         'render': true
12120     });
12121     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12122 };
12123 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12124 //Roo.Toolbar.Item.prototype = {
12125     
12126     /**
12127      * Get this item's HTML Element
12128      * @return {HTMLElement}
12129      */
12130     getEl : function(){
12131        return this.el;  
12132     },
12133
12134     // private
12135     render : function(td){
12136         
12137          this.td = td;
12138         td.appendChild(this.el);
12139         
12140         this.fireEvent('render', this);
12141     },
12142     
12143     /**
12144      * Removes and destroys this item.
12145      */
12146     destroy : function(){
12147         this.td.parentNode.removeChild(this.td);
12148     },
12149     
12150     /**
12151      * Shows this item.
12152      */
12153     show: function(){
12154         this.hidden = false;
12155         this.td.style.display = "";
12156     },
12157     
12158     /**
12159      * Hides this item.
12160      */
12161     hide: function(){
12162         this.hidden = true;
12163         this.td.style.display = "none";
12164     },
12165     
12166     /**
12167      * Convenience function for boolean show/hide.
12168      * @param {Boolean} visible true to show/false to hide
12169      */
12170     setVisible: function(visible){
12171         if(visible) {
12172             this.show();
12173         }else{
12174             this.hide();
12175         }
12176     },
12177     
12178     /**
12179      * Try to focus this item.
12180      */
12181     focus : function(){
12182         Roo.fly(this.el).focus();
12183     },
12184     
12185     /**
12186      * Disables this item.
12187      */
12188     disable : function(){
12189         Roo.fly(this.td).addClass("x-item-disabled");
12190         this.disabled = true;
12191         this.el.disabled = true;
12192     },
12193     
12194     /**
12195      * Enables this item.
12196      */
12197     enable : function(){
12198         Roo.fly(this.td).removeClass("x-item-disabled");
12199         this.disabled = false;
12200         this.el.disabled = false;
12201     }
12202 });
12203
12204
12205 /**
12206  * @class Roo.Toolbar.Separator
12207  * @extends Roo.Toolbar.Item
12208  * A simple toolbar separator class
12209  * @constructor
12210  * Creates a new Separator
12211  */
12212 Roo.Toolbar.Separator = function(cfg){
12213     
12214     var s = document.createElement("span");
12215     s.className = "ytb-sep";
12216     if (cfg) {
12217         cfg.el = s;
12218     }
12219     
12220     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12221 };
12222 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12223     enable:Roo.emptyFn,
12224     disable:Roo.emptyFn,
12225     focus:Roo.emptyFn
12226 });
12227
12228 /**
12229  * @class Roo.Toolbar.Spacer
12230  * @extends Roo.Toolbar.Item
12231  * A simple element that adds extra horizontal space to a toolbar.
12232  * @constructor
12233  * Creates a new Spacer
12234  */
12235 Roo.Toolbar.Spacer = function(cfg){
12236     var s = document.createElement("div");
12237     s.className = "ytb-spacer";
12238     if (cfg) {
12239         cfg.el = s;
12240     }
12241     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12242 };
12243 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12244     enable:Roo.emptyFn,
12245     disable:Roo.emptyFn,
12246     focus:Roo.emptyFn
12247 });
12248
12249 /**
12250  * @class Roo.Toolbar.Fill
12251  * @extends Roo.Toolbar.Spacer
12252  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12253  * @constructor
12254  * Creates a new Spacer
12255  */
12256 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12257     // private
12258     render : function(td){
12259         td.style.width = '100%';
12260         Roo.Toolbar.Fill.superclass.render.call(this, td);
12261     }
12262 });
12263
12264 /**
12265  * @class Roo.Toolbar.TextItem
12266  * @extends Roo.Toolbar.Item
12267  * A simple class that renders text directly into a toolbar.
12268  * @constructor
12269  * Creates a new TextItem
12270  * @param {String} text
12271  */
12272 Roo.Toolbar.TextItem = function(cfg){
12273     var  text = cfg || "";
12274     if (typeof(cfg) == 'object') {
12275         text = cfg.text || "";
12276     }  else {
12277         cfg = null;
12278     }
12279     var s = document.createElement("span");
12280     s.className = "ytb-text";
12281     s.innerHTML = text;
12282     if (cfg) {
12283         cfg.el  = s;
12284     }
12285     
12286     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12287 };
12288 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12289     
12290      
12291     enable:Roo.emptyFn,
12292     disable:Roo.emptyFn,
12293     focus:Roo.emptyFn
12294 });
12295
12296 /**
12297  * @class Roo.Toolbar.Button
12298  * @extends Roo.Button
12299  * A button that renders into a toolbar.
12300  * @constructor
12301  * Creates a new Button
12302  * @param {Object} config A standard {@link Roo.Button} config object
12303  */
12304 Roo.Toolbar.Button = function(config){
12305     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12306 };
12307 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12308     render : function(td){
12309         this.td = td;
12310         Roo.Toolbar.Button.superclass.render.call(this, td);
12311     },
12312     
12313     /**
12314      * Removes and destroys this button
12315      */
12316     destroy : function(){
12317         Roo.Toolbar.Button.superclass.destroy.call(this);
12318         this.td.parentNode.removeChild(this.td);
12319     },
12320     
12321     /**
12322      * Shows this button
12323      */
12324     show: function(){
12325         this.hidden = false;
12326         this.td.style.display = "";
12327     },
12328     
12329     /**
12330      * Hides this button
12331      */
12332     hide: function(){
12333         this.hidden = true;
12334         this.td.style.display = "none";
12335     },
12336
12337     /**
12338      * Disables this item
12339      */
12340     disable : function(){
12341         Roo.fly(this.td).addClass("x-item-disabled");
12342         this.disabled = true;
12343     },
12344
12345     /**
12346      * Enables this item
12347      */
12348     enable : function(){
12349         Roo.fly(this.td).removeClass("x-item-disabled");
12350         this.disabled = false;
12351     }
12352 });
12353 // backwards compat
12354 Roo.ToolbarButton = Roo.Toolbar.Button;
12355
12356 /**
12357  * @class Roo.Toolbar.SplitButton
12358  * @extends Roo.SplitButton
12359  * A menu button that renders into a toolbar.
12360  * @constructor
12361  * Creates a new SplitButton
12362  * @param {Object} config A standard {@link Roo.SplitButton} config object
12363  */
12364 Roo.Toolbar.SplitButton = function(config){
12365     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12366 };
12367 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12368     render : function(td){
12369         this.td = td;
12370         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12371     },
12372     
12373     /**
12374      * Removes and destroys this button
12375      */
12376     destroy : function(){
12377         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12378         this.td.parentNode.removeChild(this.td);
12379     },
12380     
12381     /**
12382      * Shows this button
12383      */
12384     show: function(){
12385         this.hidden = false;
12386         this.td.style.display = "";
12387     },
12388     
12389     /**
12390      * Hides this button
12391      */
12392     hide: function(){
12393         this.hidden = true;
12394         this.td.style.display = "none";
12395     }
12396 });
12397
12398 // backwards compat
12399 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12400  * Based on:
12401  * Ext JS Library 1.1.1
12402  * Copyright(c) 2006-2007, Ext JS, LLC.
12403  *
12404  * Originally Released Under LGPL - original licence link has changed is not relivant.
12405  *
12406  * Fork - LGPL
12407  * <script type="text/javascript">
12408  */
12409  
12410 /**
12411  * @class Roo.PagingToolbar
12412  * @extends Roo.Toolbar
12413  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12414  * @constructor
12415  * Create a new PagingToolbar
12416  * @param {Object} config The config object
12417  */
12418 Roo.PagingToolbar = function(el, ds, config)
12419 {
12420     // old args format still supported... - xtype is prefered..
12421     if (typeof(el) == 'object' && el.xtype) {
12422         // created from xtype...
12423         config = el;
12424         ds = el.dataSource;
12425         el = config.container;
12426     }
12427     var items = [];
12428     if (config.items) {
12429         items = config.items;
12430         config.items = [];
12431     }
12432     
12433     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12434     this.ds = ds;
12435     this.cursor = 0;
12436     this.renderButtons(this.el);
12437     this.bind(ds);
12438     
12439     // supprot items array.
12440    
12441     Roo.each(items, function(e) {
12442         this.add(Roo.factory(e));
12443     },this);
12444     
12445 };
12446
12447 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12448     /**
12449      * @cfg {Roo.data.Store} dataSource
12450      * The underlying data store providing the paged data
12451      */
12452     /**
12453      * @cfg {String/HTMLElement/Element} container
12454      * container The id or element that will contain the toolbar
12455      */
12456     /**
12457      * @cfg {Boolean} displayInfo
12458      * True to display the displayMsg (defaults to false)
12459      */
12460     /**
12461      * @cfg {Number} pageSize
12462      * The number of records to display per page (defaults to 20)
12463      */
12464     pageSize: 20,
12465     /**
12466      * @cfg {String} displayMsg
12467      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12468      */
12469     displayMsg : 'Displaying {0} - {1} of {2}',
12470     /**
12471      * @cfg {String} emptyMsg
12472      * The message to display when no records are found (defaults to "No data to display")
12473      */
12474     emptyMsg : 'No data to display',
12475     /**
12476      * Customizable piece of the default paging text (defaults to "Page")
12477      * @type String
12478      */
12479     beforePageText : "Page",
12480     /**
12481      * Customizable piece of the default paging text (defaults to "of %0")
12482      * @type String
12483      */
12484     afterPageText : "of {0}",
12485     /**
12486      * Customizable piece of the default paging text (defaults to "First Page")
12487      * @type String
12488      */
12489     firstText : "First Page",
12490     /**
12491      * Customizable piece of the default paging text (defaults to "Previous Page")
12492      * @type String
12493      */
12494     prevText : "Previous Page",
12495     /**
12496      * Customizable piece of the default paging text (defaults to "Next Page")
12497      * @type String
12498      */
12499     nextText : "Next Page",
12500     /**
12501      * Customizable piece of the default paging text (defaults to "Last Page")
12502      * @type String
12503      */
12504     lastText : "Last Page",
12505     /**
12506      * Customizable piece of the default paging text (defaults to "Refresh")
12507      * @type String
12508      */
12509     refreshText : "Refresh",
12510
12511     // private
12512     renderButtons : function(el){
12513         Roo.PagingToolbar.superclass.render.call(this, el);
12514         this.first = this.addButton({
12515             tooltip: this.firstText,
12516             cls: "x-btn-icon x-grid-page-first",
12517             disabled: true,
12518             handler: this.onClick.createDelegate(this, ["first"])
12519         });
12520         this.prev = this.addButton({
12521             tooltip: this.prevText,
12522             cls: "x-btn-icon x-grid-page-prev",
12523             disabled: true,
12524             handler: this.onClick.createDelegate(this, ["prev"])
12525         });
12526         //this.addSeparator();
12527         this.add(this.beforePageText);
12528         this.field = Roo.get(this.addDom({
12529            tag: "input",
12530            type: "text",
12531            size: "3",
12532            value: "1",
12533            cls: "x-grid-page-number"
12534         }).el);
12535         this.field.on("keydown", this.onPagingKeydown, this);
12536         this.field.on("focus", function(){this.dom.select();});
12537         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12538         this.field.setHeight(18);
12539         //this.addSeparator();
12540         this.next = this.addButton({
12541             tooltip: this.nextText,
12542             cls: "x-btn-icon x-grid-page-next",
12543             disabled: true,
12544             handler: this.onClick.createDelegate(this, ["next"])
12545         });
12546         this.last = this.addButton({
12547             tooltip: this.lastText,
12548             cls: "x-btn-icon x-grid-page-last",
12549             disabled: true,
12550             handler: this.onClick.createDelegate(this, ["last"])
12551         });
12552         //this.addSeparator();
12553         this.loading = this.addButton({
12554             tooltip: this.refreshText,
12555             cls: "x-btn-icon x-grid-loading",
12556             handler: this.onClick.createDelegate(this, ["refresh"])
12557         });
12558
12559         if(this.displayInfo){
12560             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12561         }
12562     },
12563
12564     // private
12565     updateInfo : function(){
12566         if(this.displayEl){
12567             var count = this.ds.getCount();
12568             var msg = count == 0 ?
12569                 this.emptyMsg :
12570                 String.format(
12571                     this.displayMsg,
12572                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12573                 );
12574             this.displayEl.update(msg);
12575         }
12576     },
12577
12578     // private
12579     onLoad : function(ds, r, o){
12580        this.cursor = o.params ? o.params.start : 0;
12581        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12582
12583        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12584        this.field.dom.value = ap;
12585        this.first.setDisabled(ap == 1);
12586        this.prev.setDisabled(ap == 1);
12587        this.next.setDisabled(ap == ps);
12588        this.last.setDisabled(ap == ps);
12589        this.loading.enable();
12590        this.updateInfo();
12591     },
12592
12593     // private
12594     getPageData : function(){
12595         var total = this.ds.getTotalCount();
12596         return {
12597             total : total,
12598             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12599             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12600         };
12601     },
12602
12603     // private
12604     onLoadError : function(){
12605         this.loading.enable();
12606     },
12607
12608     // private
12609     onPagingKeydown : function(e){
12610         var k = e.getKey();
12611         var d = this.getPageData();
12612         if(k == e.RETURN){
12613             var v = this.field.dom.value, pageNum;
12614             if(!v || isNaN(pageNum = parseInt(v, 10))){
12615                 this.field.dom.value = d.activePage;
12616                 return;
12617             }
12618             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12619             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12620             e.stopEvent();
12621         }
12622         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
12623         {
12624           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12625           this.field.dom.value = pageNum;
12626           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12627           e.stopEvent();
12628         }
12629         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12630         {
12631           var v = this.field.dom.value, pageNum; 
12632           var increment = (e.shiftKey) ? 10 : 1;
12633           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
12634             increment *= -1;
12635           }
12636           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12637             this.field.dom.value = d.activePage;
12638             return;
12639           }
12640           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12641           {
12642             this.field.dom.value = parseInt(v, 10) + increment;
12643             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12644             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12645           }
12646           e.stopEvent();
12647         }
12648     },
12649
12650     // private
12651     beforeLoad : function(){
12652         if(this.loading){
12653             this.loading.disable();
12654         }
12655     },
12656
12657     // private
12658     onClick : function(which){
12659         var ds = this.ds;
12660         switch(which){
12661             case "first":
12662                 ds.load({params:{start: 0, limit: this.pageSize}});
12663             break;
12664             case "prev":
12665                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12666             break;
12667             case "next":
12668                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12669             break;
12670             case "last":
12671                 var total = ds.getTotalCount();
12672                 var extra = total % this.pageSize;
12673                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12674                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12675             break;
12676             case "refresh":
12677                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12678             break;
12679         }
12680     },
12681
12682     /**
12683      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12684      * @param {Roo.data.Store} store The data store to unbind
12685      */
12686     unbind : function(ds){
12687         ds.un("beforeload", this.beforeLoad, this);
12688         ds.un("load", this.onLoad, this);
12689         ds.un("loadexception", this.onLoadError, this);
12690         ds.un("remove", this.updateInfo, this);
12691         ds.un("add", this.updateInfo, this);
12692         this.ds = undefined;
12693     },
12694
12695     /**
12696      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12697      * @param {Roo.data.Store} store The data store to bind
12698      */
12699     bind : function(ds){
12700         ds.on("beforeload", this.beforeLoad, this);
12701         ds.on("load", this.onLoad, this);
12702         ds.on("loadexception", this.onLoadError, this);
12703         ds.on("remove", this.updateInfo, this);
12704         ds.on("add", this.updateInfo, this);
12705         this.ds = ds;
12706     }
12707 });/*
12708  * Based on:
12709  * Ext JS Library 1.1.1
12710  * Copyright(c) 2006-2007, Ext JS, LLC.
12711  *
12712  * Originally Released Under LGPL - original licence link has changed is not relivant.
12713  *
12714  * Fork - LGPL
12715  * <script type="text/javascript">
12716  */
12717
12718 /**
12719  * @class Roo.Resizable
12720  * @extends Roo.util.Observable
12721  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12722  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12723  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
12724  * the element will be wrapped for you automatically.</p>
12725  * <p>Here is the list of valid resize handles:</p>
12726  * <pre>
12727 Value   Description
12728 ------  -------------------
12729  'n'     north
12730  's'     south
12731  'e'     east
12732  'w'     west
12733  'nw'    northwest
12734  'sw'    southwest
12735  'se'    southeast
12736  'ne'    northeast
12737  'hd'    horizontal drag
12738  'all'   all
12739 </pre>
12740  * <p>Here's an example showing the creation of a typical Resizable:</p>
12741  * <pre><code>
12742 var resizer = new Roo.Resizable("element-id", {
12743     handles: 'all',
12744     minWidth: 200,
12745     minHeight: 100,
12746     maxWidth: 500,
12747     maxHeight: 400,
12748     pinned: true
12749 });
12750 resizer.on("resize", myHandler);
12751 </code></pre>
12752  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12753  * resizer.east.setDisplayed(false);</p>
12754  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12755  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12756  * resize operation's new size (defaults to [0, 0])
12757  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12758  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12759  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12760  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12761  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12762  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12763  * @cfg {Number} width The width of the element in pixels (defaults to null)
12764  * @cfg {Number} height The height of the element in pixels (defaults to null)
12765  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12766  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12767  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12768  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12769  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12770  * in favor of the handles config option (defaults to false)
12771  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12772  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12773  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12774  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12775  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12776  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12777  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12778  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12779  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12780  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12781  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12782  * @constructor
12783  * Create a new resizable component
12784  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12785  * @param {Object} config configuration options
12786   */
12787 Roo.Resizable = function(el, config)
12788 {
12789     this.el = Roo.get(el);
12790
12791     if(config && config.wrap){
12792         config.resizeChild = this.el;
12793         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12794         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12795         this.el.setStyle("overflow", "hidden");
12796         this.el.setPositioning(config.resizeChild.getPositioning());
12797         config.resizeChild.clearPositioning();
12798         if(!config.width || !config.height){
12799             var csize = config.resizeChild.getSize();
12800             this.el.setSize(csize.width, csize.height);
12801         }
12802         if(config.pinned && !config.adjustments){
12803             config.adjustments = "auto";
12804         }
12805     }
12806
12807     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12808     this.proxy.unselectable();
12809     this.proxy.enableDisplayMode('block');
12810
12811     Roo.apply(this, config);
12812
12813     if(this.pinned){
12814         this.disableTrackOver = true;
12815         this.el.addClass("x-resizable-pinned");
12816     }
12817     // if the element isn't positioned, make it relative
12818     var position = this.el.getStyle("position");
12819     if(position != "absolute" && position != "fixed"){
12820         this.el.setStyle("position", "relative");
12821     }
12822     if(!this.handles){ // no handles passed, must be legacy style
12823         this.handles = 's,e,se';
12824         if(this.multiDirectional){
12825             this.handles += ',n,w';
12826         }
12827     }
12828     if(this.handles == "all"){
12829         this.handles = "n s e w ne nw se sw";
12830     }
12831     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12832     var ps = Roo.Resizable.positions;
12833     for(var i = 0, len = hs.length; i < len; i++){
12834         if(hs[i] && ps[hs[i]]){
12835             var pos = ps[hs[i]];
12836             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12837         }
12838     }
12839     // legacy
12840     this.corner = this.southeast;
12841     
12842     // updateBox = the box can move..
12843     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12844         this.updateBox = true;
12845     }
12846
12847     this.activeHandle = null;
12848
12849     if(this.resizeChild){
12850         if(typeof this.resizeChild == "boolean"){
12851             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12852         }else{
12853             this.resizeChild = Roo.get(this.resizeChild, true);
12854         }
12855     }
12856     
12857     if(this.adjustments == "auto"){
12858         var rc = this.resizeChild;
12859         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12860         if(rc && (hw || hn)){
12861             rc.position("relative");
12862             rc.setLeft(hw ? hw.el.getWidth() : 0);
12863             rc.setTop(hn ? hn.el.getHeight() : 0);
12864         }
12865         this.adjustments = [
12866             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12867             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12868         ];
12869     }
12870
12871     if(this.draggable){
12872         this.dd = this.dynamic ?
12873             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12874         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12875     }
12876
12877     // public events
12878     this.addEvents({
12879         /**
12880          * @event beforeresize
12881          * Fired before resize is allowed. Set enabled to false to cancel resize.
12882          * @param {Roo.Resizable} this
12883          * @param {Roo.EventObject} e The mousedown event
12884          */
12885         "beforeresize" : true,
12886         /**
12887          * @event resizing
12888          * Fired a resizing.
12889          * @param {Roo.Resizable} this
12890          * @param {Number} x The new x position
12891          * @param {Number} y The new y position
12892          * @param {Number} w The new w width
12893          * @param {Number} h The new h hight
12894          * @param {Roo.EventObject} e The mouseup event
12895          */
12896         "resizing" : true,
12897         /**
12898          * @event resize
12899          * Fired after a resize.
12900          * @param {Roo.Resizable} this
12901          * @param {Number} width The new width
12902          * @param {Number} height The new height
12903          * @param {Roo.EventObject} e The mouseup event
12904          */
12905         "resize" : true
12906     });
12907
12908     if(this.width !== null && this.height !== null){
12909         this.resizeTo(this.width, this.height);
12910     }else{
12911         this.updateChildSize();
12912     }
12913     if(Roo.isIE){
12914         this.el.dom.style.zoom = 1;
12915     }
12916     Roo.Resizable.superclass.constructor.call(this);
12917 };
12918
12919 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12920         resizeChild : false,
12921         adjustments : [0, 0],
12922         minWidth : 5,
12923         minHeight : 5,
12924         maxWidth : 10000,
12925         maxHeight : 10000,
12926         enabled : true,
12927         animate : false,
12928         duration : .35,
12929         dynamic : false,
12930         handles : false,
12931         multiDirectional : false,
12932         disableTrackOver : false,
12933         easing : 'easeOutStrong',
12934         widthIncrement : 0,
12935         heightIncrement : 0,
12936         pinned : false,
12937         width : null,
12938         height : null,
12939         preserveRatio : false,
12940         transparent: false,
12941         minX: 0,
12942         minY: 0,
12943         draggable: false,
12944
12945         /**
12946          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12947          */
12948         constrainTo: undefined,
12949         /**
12950          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12951          */
12952         resizeRegion: undefined,
12953
12954
12955     /**
12956      * Perform a manual resize
12957      * @param {Number} width
12958      * @param {Number} height
12959      */
12960     resizeTo : function(width, height){
12961         this.el.setSize(width, height);
12962         this.updateChildSize();
12963         this.fireEvent("resize", this, width, height, null);
12964     },
12965
12966     // private
12967     startSizing : function(e, handle){
12968         this.fireEvent("beforeresize", this, e);
12969         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12970
12971             if(!this.overlay){
12972                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12973                 this.overlay.unselectable();
12974                 this.overlay.enableDisplayMode("block");
12975                 this.overlay.on("mousemove", this.onMouseMove, this);
12976                 this.overlay.on("mouseup", this.onMouseUp, this);
12977             }
12978             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12979
12980             this.resizing = true;
12981             this.startBox = this.el.getBox();
12982             this.startPoint = e.getXY();
12983             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12984                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12985
12986             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12987             this.overlay.show();
12988
12989             if(this.constrainTo) {
12990                 var ct = Roo.get(this.constrainTo);
12991                 this.resizeRegion = ct.getRegion().adjust(
12992                     ct.getFrameWidth('t'),
12993                     ct.getFrameWidth('l'),
12994                     -ct.getFrameWidth('b'),
12995                     -ct.getFrameWidth('r')
12996                 );
12997             }
12998
12999             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13000             this.proxy.show();
13001             this.proxy.setBox(this.startBox);
13002             if(!this.dynamic){
13003                 this.proxy.setStyle('visibility', 'visible');
13004             }
13005         }
13006     },
13007
13008     // private
13009     onMouseDown : function(handle, e){
13010         if(this.enabled){
13011             e.stopEvent();
13012             this.activeHandle = handle;
13013             this.startSizing(e, handle);
13014         }
13015     },
13016
13017     // private
13018     onMouseUp : function(e){
13019         var size = this.resizeElement();
13020         this.resizing = false;
13021         this.handleOut();
13022         this.overlay.hide();
13023         this.proxy.hide();
13024         this.fireEvent("resize", this, size.width, size.height, e);
13025     },
13026
13027     // private
13028     updateChildSize : function(){
13029         
13030         if(this.resizeChild){
13031             var el = this.el;
13032             var child = this.resizeChild;
13033             var adj = this.adjustments;
13034             if(el.dom.offsetWidth){
13035                 var b = el.getSize(true);
13036                 child.setSize(b.width+adj[0], b.height+adj[1]);
13037             }
13038             // Second call here for IE
13039             // The first call enables instant resizing and
13040             // the second call corrects scroll bars if they
13041             // exist
13042             if(Roo.isIE){
13043                 setTimeout(function(){
13044                     if(el.dom.offsetWidth){
13045                         var b = el.getSize(true);
13046                         child.setSize(b.width+adj[0], b.height+adj[1]);
13047                     }
13048                 }, 10);
13049             }
13050         }
13051     },
13052
13053     // private
13054     snap : function(value, inc, min){
13055         if(!inc || !value) {
13056             return value;
13057         }
13058         var newValue = value;
13059         var m = value % inc;
13060         if(m > 0){
13061             if(m > (inc/2)){
13062                 newValue = value + (inc-m);
13063             }else{
13064                 newValue = value - m;
13065             }
13066         }
13067         return Math.max(min, newValue);
13068     },
13069
13070     // private
13071     resizeElement : function(){
13072         var box = this.proxy.getBox();
13073         if(this.updateBox){
13074             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13075         }else{
13076             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13077         }
13078         this.updateChildSize();
13079         if(!this.dynamic){
13080             this.proxy.hide();
13081         }
13082         return box;
13083     },
13084
13085     // private
13086     constrain : function(v, diff, m, mx){
13087         if(v - diff < m){
13088             diff = v - m;
13089         }else if(v - diff > mx){
13090             diff = mx - v;
13091         }
13092         return diff;
13093     },
13094
13095     // private
13096     onMouseMove : function(e){
13097         
13098         if(this.enabled){
13099             try{// try catch so if something goes wrong the user doesn't get hung
13100
13101             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13102                 return;
13103             }
13104
13105             //var curXY = this.startPoint;
13106             var curSize = this.curSize || this.startBox;
13107             var x = this.startBox.x, y = this.startBox.y;
13108             var ox = x, oy = y;
13109             var w = curSize.width, h = curSize.height;
13110             var ow = w, oh = h;
13111             var mw = this.minWidth, mh = this.minHeight;
13112             var mxw = this.maxWidth, mxh = this.maxHeight;
13113             var wi = this.widthIncrement;
13114             var hi = this.heightIncrement;
13115
13116             var eventXY = e.getXY();
13117             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13118             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13119
13120             var pos = this.activeHandle.position;
13121
13122             switch(pos){
13123                 case "east":
13124                     w += diffX;
13125                     w = Math.min(Math.max(mw, w), mxw);
13126                     break;
13127              
13128                 case "south":
13129                     h += diffY;
13130                     h = Math.min(Math.max(mh, h), mxh);
13131                     break;
13132                 case "southeast":
13133                     w += diffX;
13134                     h += diffY;
13135                     w = Math.min(Math.max(mw, w), mxw);
13136                     h = Math.min(Math.max(mh, h), mxh);
13137                     break;
13138                 case "north":
13139                     diffY = this.constrain(h, diffY, mh, mxh);
13140                     y += diffY;
13141                     h -= diffY;
13142                     break;
13143                 case "hdrag":
13144                     
13145                     if (wi) {
13146                         var adiffX = Math.abs(diffX);
13147                         var sub = (adiffX % wi); // how much 
13148                         if (sub > (wi/2)) { // far enough to snap
13149                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13150                         } else {
13151                             // remove difference.. 
13152                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13153                         }
13154                     }
13155                     x += diffX;
13156                     x = Math.max(this.minX, x);
13157                     break;
13158                 case "west":
13159                     diffX = this.constrain(w, diffX, mw, mxw);
13160                     x += diffX;
13161                     w -= diffX;
13162                     break;
13163                 case "northeast":
13164                     w += diffX;
13165                     w = Math.min(Math.max(mw, w), mxw);
13166                     diffY = this.constrain(h, diffY, mh, mxh);
13167                     y += diffY;
13168                     h -= diffY;
13169                     break;
13170                 case "northwest":
13171                     diffX = this.constrain(w, diffX, mw, mxw);
13172                     diffY = this.constrain(h, diffY, mh, mxh);
13173                     y += diffY;
13174                     h -= diffY;
13175                     x += diffX;
13176                     w -= diffX;
13177                     break;
13178                case "southwest":
13179                     diffX = this.constrain(w, diffX, mw, mxw);
13180                     h += diffY;
13181                     h = Math.min(Math.max(mh, h), mxh);
13182                     x += diffX;
13183                     w -= diffX;
13184                     break;
13185             }
13186
13187             var sw = this.snap(w, wi, mw);
13188             var sh = this.snap(h, hi, mh);
13189             if(sw != w || sh != h){
13190                 switch(pos){
13191                     case "northeast":
13192                         y -= sh - h;
13193                     break;
13194                     case "north":
13195                         y -= sh - h;
13196                         break;
13197                     case "southwest":
13198                         x -= sw - w;
13199                     break;
13200                     case "west":
13201                         x -= sw - w;
13202                         break;
13203                     case "northwest":
13204                         x -= sw - w;
13205                         y -= sh - h;
13206                     break;
13207                 }
13208                 w = sw;
13209                 h = sh;
13210             }
13211
13212             if(this.preserveRatio){
13213                 switch(pos){
13214                     case "southeast":
13215                     case "east":
13216                         h = oh * (w/ow);
13217                         h = Math.min(Math.max(mh, h), mxh);
13218                         w = ow * (h/oh);
13219                        break;
13220                     case "south":
13221                         w = ow * (h/oh);
13222                         w = Math.min(Math.max(mw, w), mxw);
13223                         h = oh * (w/ow);
13224                         break;
13225                     case "northeast":
13226                         w = ow * (h/oh);
13227                         w = Math.min(Math.max(mw, w), mxw);
13228                         h = oh * (w/ow);
13229                     break;
13230                     case "north":
13231                         var tw = w;
13232                         w = ow * (h/oh);
13233                         w = Math.min(Math.max(mw, w), mxw);
13234                         h = oh * (w/ow);
13235                         x += (tw - w) / 2;
13236                         break;
13237                     case "southwest":
13238                         h = oh * (w/ow);
13239                         h = Math.min(Math.max(mh, h), mxh);
13240                         var tw = w;
13241                         w = ow * (h/oh);
13242                         x += tw - w;
13243                         break;
13244                     case "west":
13245                         var th = h;
13246                         h = oh * (w/ow);
13247                         h = Math.min(Math.max(mh, h), mxh);
13248                         y += (th - h) / 2;
13249                         var tw = w;
13250                         w = ow * (h/oh);
13251                         x += tw - w;
13252                        break;
13253                     case "northwest":
13254                         var tw = w;
13255                         var th = h;
13256                         h = oh * (w/ow);
13257                         h = Math.min(Math.max(mh, h), mxh);
13258                         w = ow * (h/oh);
13259                         y += th - h;
13260                         x += tw - w;
13261                        break;
13262
13263                 }
13264             }
13265             if (pos == 'hdrag') {
13266                 w = ow;
13267             }
13268             this.proxy.setBounds(x, y, w, h);
13269             if(this.dynamic){
13270                 this.resizeElement();
13271             }
13272             }catch(e){}
13273         }
13274         this.fireEvent("resizing", this, x, y, w, h, e);
13275     },
13276
13277     // private
13278     handleOver : function(){
13279         if(this.enabled){
13280             this.el.addClass("x-resizable-over");
13281         }
13282     },
13283
13284     // private
13285     handleOut : function(){
13286         if(!this.resizing){
13287             this.el.removeClass("x-resizable-over");
13288         }
13289     },
13290
13291     /**
13292      * Returns the element this component is bound to.
13293      * @return {Roo.Element}
13294      */
13295     getEl : function(){
13296         return this.el;
13297     },
13298
13299     /**
13300      * Returns the resizeChild element (or null).
13301      * @return {Roo.Element}
13302      */
13303     getResizeChild : function(){
13304         return this.resizeChild;
13305     },
13306     groupHandler : function()
13307     {
13308         
13309     },
13310     /**
13311      * Destroys this resizable. If the element was wrapped and
13312      * removeEl is not true then the element remains.
13313      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13314      */
13315     destroy : function(removeEl){
13316         this.proxy.remove();
13317         if(this.overlay){
13318             this.overlay.removeAllListeners();
13319             this.overlay.remove();
13320         }
13321         var ps = Roo.Resizable.positions;
13322         for(var k in ps){
13323             if(typeof ps[k] != "function" && this[ps[k]]){
13324                 var h = this[ps[k]];
13325                 h.el.removeAllListeners();
13326                 h.el.remove();
13327             }
13328         }
13329         if(removeEl){
13330             this.el.update("");
13331             this.el.remove();
13332         }
13333     }
13334 });
13335
13336 // private
13337 // hash to map config positions to true positions
13338 Roo.Resizable.positions = {
13339     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13340     hd: "hdrag"
13341 };
13342
13343 // private
13344 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13345     if(!this.tpl){
13346         // only initialize the template if resizable is used
13347         var tpl = Roo.DomHelper.createTemplate(
13348             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13349         );
13350         tpl.compile();
13351         Roo.Resizable.Handle.prototype.tpl = tpl;
13352     }
13353     this.position = pos;
13354     this.rz = rz;
13355     // show north drag fro topdra
13356     var handlepos = pos == 'hdrag' ? 'north' : pos;
13357     
13358     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13359     if (pos == 'hdrag') {
13360         this.el.setStyle('cursor', 'pointer');
13361     }
13362     this.el.unselectable();
13363     if(transparent){
13364         this.el.setOpacity(0);
13365     }
13366     this.el.on("mousedown", this.onMouseDown, this);
13367     if(!disableTrackOver){
13368         this.el.on("mouseover", this.onMouseOver, this);
13369         this.el.on("mouseout", this.onMouseOut, this);
13370     }
13371 };
13372
13373 // private
13374 Roo.Resizable.Handle.prototype = {
13375     afterResize : function(rz){
13376         Roo.log('after?');
13377         // do nothing
13378     },
13379     // private
13380     onMouseDown : function(e){
13381         this.rz.onMouseDown(this, e);
13382     },
13383     // private
13384     onMouseOver : function(e){
13385         this.rz.handleOver(this, e);
13386     },
13387     // private
13388     onMouseOut : function(e){
13389         this.rz.handleOut(this, e);
13390     }
13391 };/*
13392  * Based on:
13393  * Ext JS Library 1.1.1
13394  * Copyright(c) 2006-2007, Ext JS, LLC.
13395  *
13396  * Originally Released Under LGPL - original licence link has changed is not relivant.
13397  *
13398  * Fork - LGPL
13399  * <script type="text/javascript">
13400  */
13401
13402 /**
13403  * @class Roo.Editor
13404  * @extends Roo.Component
13405  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13406  * @constructor
13407  * Create a new Editor
13408  * @param {Roo.form.Field} field The Field object (or descendant)
13409  * @param {Object} config The config object
13410  */
13411 Roo.Editor = function(field, config){
13412     Roo.Editor.superclass.constructor.call(this, config);
13413     this.field = field;
13414     this.addEvents({
13415         /**
13416              * @event beforestartedit
13417              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13418              * false from the handler of this event.
13419              * @param {Editor} this
13420              * @param {Roo.Element} boundEl The underlying element bound to this editor
13421              * @param {Mixed} value The field value being set
13422              */
13423         "beforestartedit" : true,
13424         /**
13425              * @event startedit
13426              * Fires when this editor is displayed
13427              * @param {Roo.Element} boundEl The underlying element bound to this editor
13428              * @param {Mixed} value The starting field value
13429              */
13430         "startedit" : true,
13431         /**
13432              * @event beforecomplete
13433              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13434              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13435              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13436              * event will not fire since no edit actually occurred.
13437              * @param {Editor} this
13438              * @param {Mixed} value The current field value
13439              * @param {Mixed} startValue The original field value
13440              */
13441         "beforecomplete" : true,
13442         /**
13443              * @event complete
13444              * Fires after editing is complete and any changed value has been written to the underlying field.
13445              * @param {Editor} this
13446              * @param {Mixed} value The current field value
13447              * @param {Mixed} startValue The original field value
13448              */
13449         "complete" : true,
13450         /**
13451          * @event specialkey
13452          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13453          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13454          * @param {Roo.form.Field} this
13455          * @param {Roo.EventObject} e The event object
13456          */
13457         "specialkey" : true
13458     });
13459 };
13460
13461 Roo.extend(Roo.Editor, Roo.Component, {
13462     /**
13463      * @cfg {Boolean/String} autosize
13464      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13465      * or "height" to adopt the height only (defaults to false)
13466      */
13467     /**
13468      * @cfg {Boolean} revertInvalid
13469      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13470      * validation fails (defaults to true)
13471      */
13472     /**
13473      * @cfg {Boolean} ignoreNoChange
13474      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13475      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13476      * will never be ignored.
13477      */
13478     /**
13479      * @cfg {Boolean} hideEl
13480      * False to keep the bound element visible while the editor is displayed (defaults to true)
13481      */
13482     /**
13483      * @cfg {Mixed} value
13484      * The data value of the underlying field (defaults to "")
13485      */
13486     value : "",
13487     /**
13488      * @cfg {String} alignment
13489      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13490      */
13491     alignment: "c-c?",
13492     /**
13493      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13494      * for bottom-right shadow (defaults to "frame")
13495      */
13496     shadow : "frame",
13497     /**
13498      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13499      */
13500     constrain : false,
13501     /**
13502      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13503      */
13504     completeOnEnter : false,
13505     /**
13506      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13507      */
13508     cancelOnEsc : false,
13509     /**
13510      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13511      */
13512     updateEl : false,
13513
13514     // private
13515     onRender : function(ct, position){
13516         this.el = new Roo.Layer({
13517             shadow: this.shadow,
13518             cls: "x-editor",
13519             parentEl : ct,
13520             shim : this.shim,
13521             shadowOffset:4,
13522             id: this.id,
13523             constrain: this.constrain
13524         });
13525         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13526         if(this.field.msgTarget != 'title'){
13527             this.field.msgTarget = 'qtip';
13528         }
13529         this.field.render(this.el);
13530         if(Roo.isGecko){
13531             this.field.el.dom.setAttribute('autocomplete', 'off');
13532         }
13533         this.field.on("specialkey", this.onSpecialKey, this);
13534         if(this.swallowKeys){
13535             this.field.el.swallowEvent(['keydown','keypress']);
13536         }
13537         this.field.show();
13538         this.field.on("blur", this.onBlur, this);
13539         if(this.field.grow){
13540             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13541         }
13542     },
13543
13544     onSpecialKey : function(field, e)
13545     {
13546         //Roo.log('editor onSpecialKey');
13547         if(this.completeOnEnter && e.getKey() == e.ENTER){
13548             e.stopEvent();
13549             this.completeEdit();
13550             return;
13551         }
13552         // do not fire special key otherwise it might hide close the editor...
13553         if(e.getKey() == e.ENTER){    
13554             return;
13555         }
13556         if(this.cancelOnEsc && e.getKey() == e.ESC){
13557             this.cancelEdit();
13558             return;
13559         } 
13560         this.fireEvent('specialkey', field, e);
13561     
13562     },
13563
13564     /**
13565      * Starts the editing process and shows the editor.
13566      * @param {String/HTMLElement/Element} el The element to edit
13567      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13568       * to the innerHTML of el.
13569      */
13570     startEdit : function(el, value){
13571         if(this.editing){
13572             this.completeEdit();
13573         }
13574         this.boundEl = Roo.get(el);
13575         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13576         if(!this.rendered){
13577             this.render(this.parentEl || document.body);
13578         }
13579         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13580             return;
13581         }
13582         this.startValue = v;
13583         this.field.setValue(v);
13584         if(this.autoSize){
13585             var sz = this.boundEl.getSize();
13586             switch(this.autoSize){
13587                 case "width":
13588                 this.setSize(sz.width,  "");
13589                 break;
13590                 case "height":
13591                 this.setSize("",  sz.height);
13592                 break;
13593                 default:
13594                 this.setSize(sz.width,  sz.height);
13595             }
13596         }
13597         this.el.alignTo(this.boundEl, this.alignment);
13598         this.editing = true;
13599         if(Roo.QuickTips){
13600             Roo.QuickTips.disable();
13601         }
13602         this.show();
13603     },
13604
13605     /**
13606      * Sets the height and width of this editor.
13607      * @param {Number} width The new width
13608      * @param {Number} height The new height
13609      */
13610     setSize : function(w, h){
13611         this.field.setSize(w, h);
13612         if(this.el){
13613             this.el.sync();
13614         }
13615     },
13616
13617     /**
13618      * Realigns the editor to the bound field based on the current alignment config value.
13619      */
13620     realign : function(){
13621         this.el.alignTo(this.boundEl, this.alignment);
13622     },
13623
13624     /**
13625      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13626      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13627      */
13628     completeEdit : function(remainVisible){
13629         if(!this.editing){
13630             return;
13631         }
13632         var v = this.getValue();
13633         if(this.revertInvalid !== false && !this.field.isValid()){
13634             v = this.startValue;
13635             this.cancelEdit(true);
13636         }
13637         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13638             this.editing = false;
13639             this.hide();
13640             return;
13641         }
13642         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13643             this.editing = false;
13644             if(this.updateEl && this.boundEl){
13645                 this.boundEl.update(v);
13646             }
13647             if(remainVisible !== true){
13648                 this.hide();
13649             }
13650             this.fireEvent("complete", this, v, this.startValue);
13651         }
13652     },
13653
13654     // private
13655     onShow : function(){
13656         this.el.show();
13657         if(this.hideEl !== false){
13658             this.boundEl.hide();
13659         }
13660         this.field.show();
13661         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13662             this.fixIEFocus = true;
13663             this.deferredFocus.defer(50, this);
13664         }else{
13665             this.field.focus();
13666         }
13667         this.fireEvent("startedit", this.boundEl, this.startValue);
13668     },
13669
13670     deferredFocus : function(){
13671         if(this.editing){
13672             this.field.focus();
13673         }
13674     },
13675
13676     /**
13677      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13678      * reverted to the original starting value.
13679      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13680      * cancel (defaults to false)
13681      */
13682     cancelEdit : function(remainVisible){
13683         if(this.editing){
13684             this.setValue(this.startValue);
13685             if(remainVisible !== true){
13686                 this.hide();
13687             }
13688         }
13689     },
13690
13691     // private
13692     onBlur : function(){
13693         if(this.allowBlur !== true && this.editing){
13694             this.completeEdit();
13695         }
13696     },
13697
13698     // private
13699     onHide : function(){
13700         if(this.editing){
13701             this.completeEdit();
13702             return;
13703         }
13704         this.field.blur();
13705         if(this.field.collapse){
13706             this.field.collapse();
13707         }
13708         this.el.hide();
13709         if(this.hideEl !== false){
13710             this.boundEl.show();
13711         }
13712         if(Roo.QuickTips){
13713             Roo.QuickTips.enable();
13714         }
13715     },
13716
13717     /**
13718      * Sets the data value of the editor
13719      * @param {Mixed} value Any valid value supported by the underlying field
13720      */
13721     setValue : function(v){
13722         this.field.setValue(v);
13723     },
13724
13725     /**
13726      * Gets the data value of the editor
13727      * @return {Mixed} The data value
13728      */
13729     getValue : function(){
13730         return this.field.getValue();
13731     }
13732 });/*
13733  * Based on:
13734  * Ext JS Library 1.1.1
13735  * Copyright(c) 2006-2007, Ext JS, LLC.
13736  *
13737  * Originally Released Under LGPL - original licence link has changed is not relivant.
13738  *
13739  * Fork - LGPL
13740  * <script type="text/javascript">
13741  */
13742  
13743 /**
13744  * @class Roo.BasicDialog
13745  * @extends Roo.util.Observable
13746  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13747  * <pre><code>
13748 var dlg = new Roo.BasicDialog("my-dlg", {
13749     height: 200,
13750     width: 300,
13751     minHeight: 100,
13752     minWidth: 150,
13753     modal: true,
13754     proxyDrag: true,
13755     shadow: true
13756 });
13757 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13758 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13759 dlg.addButton('Cancel', dlg.hide, dlg);
13760 dlg.show();
13761 </code></pre>
13762   <b>A Dialog should always be a direct child of the body element.</b>
13763  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13764  * @cfg {String} title Default text to display in the title bar (defaults to null)
13765  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13766  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13767  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13768  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13769  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13770  * (defaults to null with no animation)
13771  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13772  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13773  * property for valid values (defaults to 'all')
13774  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13775  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13776  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13777  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13778  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13779  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13780  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13781  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13782  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13783  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13784  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13785  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13786  * draggable = true (defaults to false)
13787  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13788  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13789  * shadow (defaults to false)
13790  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13791  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13792  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13793  * @cfg {Array} buttons Array of buttons
13794  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13795  * @constructor
13796  * Create a new BasicDialog.
13797  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13798  * @param {Object} config Configuration options
13799  */
13800 Roo.BasicDialog = function(el, config){
13801     this.el = Roo.get(el);
13802     var dh = Roo.DomHelper;
13803     if(!this.el && config && config.autoCreate){
13804         if(typeof config.autoCreate == "object"){
13805             if(!config.autoCreate.id){
13806                 config.autoCreate.id = el;
13807             }
13808             this.el = dh.append(document.body,
13809                         config.autoCreate, true);
13810         }else{
13811             this.el = dh.append(document.body,
13812                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13813         }
13814     }
13815     el = this.el;
13816     el.setDisplayed(true);
13817     el.hide = this.hideAction;
13818     this.id = el.id;
13819     el.addClass("x-dlg");
13820
13821     Roo.apply(this, config);
13822
13823     this.proxy = el.createProxy("x-dlg-proxy");
13824     this.proxy.hide = this.hideAction;
13825     this.proxy.setOpacity(.5);
13826     this.proxy.hide();
13827
13828     if(config.width){
13829         el.setWidth(config.width);
13830     }
13831     if(config.height){
13832         el.setHeight(config.height);
13833     }
13834     this.size = el.getSize();
13835     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13836         this.xy = [config.x,config.y];
13837     }else{
13838         this.xy = el.getCenterXY(true);
13839     }
13840     /** The header element @type Roo.Element */
13841     this.header = el.child("> .x-dlg-hd");
13842     /** The body element @type Roo.Element */
13843     this.body = el.child("> .x-dlg-bd");
13844     /** The footer element @type Roo.Element */
13845     this.footer = el.child("> .x-dlg-ft");
13846
13847     if(!this.header){
13848         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13849     }
13850     if(!this.body){
13851         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13852     }
13853
13854     this.header.unselectable();
13855     if(this.title){
13856         this.header.update(this.title);
13857     }
13858     // this element allows the dialog to be focused for keyboard event
13859     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13860     this.focusEl.swallowEvent("click", true);
13861
13862     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13863
13864     // wrap the body and footer for special rendering
13865     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13866     if(this.footer){
13867         this.bwrap.dom.appendChild(this.footer.dom);
13868     }
13869
13870     this.bg = this.el.createChild({
13871         tag: "div", cls:"x-dlg-bg",
13872         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13873     });
13874     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13875
13876
13877     if(this.autoScroll !== false && !this.autoTabs){
13878         this.body.setStyle("overflow", "auto");
13879     }
13880
13881     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13882
13883     if(this.closable !== false){
13884         this.el.addClass("x-dlg-closable");
13885         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13886         this.close.on("click", this.closeClick, this);
13887         this.close.addClassOnOver("x-dlg-close-over");
13888     }
13889     if(this.collapsible !== false){
13890         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13891         this.collapseBtn.on("click", this.collapseClick, this);
13892         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13893         this.header.on("dblclick", this.collapseClick, this);
13894     }
13895     if(this.resizable !== false){
13896         this.el.addClass("x-dlg-resizable");
13897         this.resizer = new Roo.Resizable(el, {
13898             minWidth: this.minWidth || 80,
13899             minHeight:this.minHeight || 80,
13900             handles: this.resizeHandles || "all",
13901             pinned: true
13902         });
13903         this.resizer.on("beforeresize", this.beforeResize, this);
13904         this.resizer.on("resize", this.onResize, this);
13905     }
13906     if(this.draggable !== false){
13907         el.addClass("x-dlg-draggable");
13908         if (!this.proxyDrag) {
13909             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13910         }
13911         else {
13912             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13913         }
13914         dd.setHandleElId(this.header.id);
13915         dd.endDrag = this.endMove.createDelegate(this);
13916         dd.startDrag = this.startMove.createDelegate(this);
13917         dd.onDrag = this.onDrag.createDelegate(this);
13918         dd.scroll = false;
13919         this.dd = dd;
13920     }
13921     if(this.modal){
13922         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13923         this.mask.enableDisplayMode("block");
13924         this.mask.hide();
13925         this.el.addClass("x-dlg-modal");
13926     }
13927     if(this.shadow){
13928         this.shadow = new Roo.Shadow({
13929             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13930             offset : this.shadowOffset
13931         });
13932     }else{
13933         this.shadowOffset = 0;
13934     }
13935     if(Roo.useShims && this.shim !== false){
13936         this.shim = this.el.createShim();
13937         this.shim.hide = this.hideAction;
13938         this.shim.hide();
13939     }else{
13940         this.shim = false;
13941     }
13942     if(this.autoTabs){
13943         this.initTabs();
13944     }
13945     if (this.buttons) { 
13946         var bts= this.buttons;
13947         this.buttons = [];
13948         Roo.each(bts, function(b) {
13949             this.addButton(b);
13950         }, this);
13951     }
13952     
13953     
13954     this.addEvents({
13955         /**
13956          * @event keydown
13957          * Fires when a key is pressed
13958          * @param {Roo.BasicDialog} this
13959          * @param {Roo.EventObject} e
13960          */
13961         "keydown" : true,
13962         /**
13963          * @event move
13964          * Fires when this dialog is moved by the user.
13965          * @param {Roo.BasicDialog} this
13966          * @param {Number} x The new page X
13967          * @param {Number} y The new page Y
13968          */
13969         "move" : true,
13970         /**
13971          * @event resize
13972          * Fires when this dialog is resized by the user.
13973          * @param {Roo.BasicDialog} this
13974          * @param {Number} width The new width
13975          * @param {Number} height The new height
13976          */
13977         "resize" : true,
13978         /**
13979          * @event beforehide
13980          * Fires before this dialog is hidden.
13981          * @param {Roo.BasicDialog} this
13982          */
13983         "beforehide" : true,
13984         /**
13985          * @event hide
13986          * Fires when this dialog is hidden.
13987          * @param {Roo.BasicDialog} this
13988          */
13989         "hide" : true,
13990         /**
13991          * @event beforeshow
13992          * Fires before this dialog is shown.
13993          * @param {Roo.BasicDialog} this
13994          */
13995         "beforeshow" : true,
13996         /**
13997          * @event show
13998          * Fires when this dialog is shown.
13999          * @param {Roo.BasicDialog} this
14000          */
14001         "show" : true
14002     });
14003     el.on("keydown", this.onKeyDown, this);
14004     el.on("mousedown", this.toFront, this);
14005     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14006     this.el.hide();
14007     Roo.DialogManager.register(this);
14008     Roo.BasicDialog.superclass.constructor.call(this);
14009 };
14010
14011 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14012     shadowOffset: Roo.isIE ? 6 : 5,
14013     minHeight: 80,
14014     minWidth: 200,
14015     minButtonWidth: 75,
14016     defaultButton: null,
14017     buttonAlign: "right",
14018     tabTag: 'div',
14019     firstShow: true,
14020
14021     /**
14022      * Sets the dialog title text
14023      * @param {String} text The title text to display
14024      * @return {Roo.BasicDialog} this
14025      */
14026     setTitle : function(text){
14027         this.header.update(text);
14028         return this;
14029     },
14030
14031     // private
14032     closeClick : function(){
14033         this.hide();
14034     },
14035
14036     // private
14037     collapseClick : function(){
14038         this[this.collapsed ? "expand" : "collapse"]();
14039     },
14040
14041     /**
14042      * Collapses the dialog to its minimized state (only the title bar is visible).
14043      * Equivalent to the user clicking the collapse dialog button.
14044      */
14045     collapse : function(){
14046         if(!this.collapsed){
14047             this.collapsed = true;
14048             this.el.addClass("x-dlg-collapsed");
14049             this.restoreHeight = this.el.getHeight();
14050             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14051         }
14052     },
14053
14054     /**
14055      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14056      * clicking the expand dialog button.
14057      */
14058     expand : function(){
14059         if(this.collapsed){
14060             this.collapsed = false;
14061             this.el.removeClass("x-dlg-collapsed");
14062             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14063         }
14064     },
14065
14066     /**
14067      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14068      * @return {Roo.TabPanel} The tabs component
14069      */
14070     initTabs : function(){
14071         var tabs = this.getTabs();
14072         while(tabs.getTab(0)){
14073             tabs.removeTab(0);
14074         }
14075         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14076             var dom = el.dom;
14077             tabs.addTab(Roo.id(dom), dom.title);
14078             dom.title = "";
14079         });
14080         tabs.activate(0);
14081         return tabs;
14082     },
14083
14084     // private
14085     beforeResize : function(){
14086         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14087     },
14088
14089     // private
14090     onResize : function(){
14091         this.refreshSize();
14092         this.syncBodyHeight();
14093         this.adjustAssets();
14094         this.focus();
14095         this.fireEvent("resize", this, this.size.width, this.size.height);
14096     },
14097
14098     // private
14099     onKeyDown : function(e){
14100         if(this.isVisible()){
14101             this.fireEvent("keydown", this, e);
14102         }
14103     },
14104
14105     /**
14106      * Resizes the dialog.
14107      * @param {Number} width
14108      * @param {Number} height
14109      * @return {Roo.BasicDialog} this
14110      */
14111     resizeTo : function(width, height){
14112         this.el.setSize(width, height);
14113         this.size = {width: width, height: height};
14114         this.syncBodyHeight();
14115         if(this.fixedcenter){
14116             this.center();
14117         }
14118         if(this.isVisible()){
14119             this.constrainXY();
14120             this.adjustAssets();
14121         }
14122         this.fireEvent("resize", this, width, height);
14123         return this;
14124     },
14125
14126
14127     /**
14128      * Resizes the dialog to fit the specified content size.
14129      * @param {Number} width
14130      * @param {Number} height
14131      * @return {Roo.BasicDialog} this
14132      */
14133     setContentSize : function(w, h){
14134         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14135         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14136         //if(!this.el.isBorderBox()){
14137             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14138             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14139         //}
14140         if(this.tabs){
14141             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14142             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14143         }
14144         this.resizeTo(w, h);
14145         return this;
14146     },
14147
14148     /**
14149      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14150      * executed in response to a particular key being pressed while the dialog is active.
14151      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14152      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14153      * @param {Function} fn The function to call
14154      * @param {Object} scope (optional) The scope of the function
14155      * @return {Roo.BasicDialog} this
14156      */
14157     addKeyListener : function(key, fn, scope){
14158         var keyCode, shift, ctrl, alt;
14159         if(typeof key == "object" && !(key instanceof Array)){
14160             keyCode = key["key"];
14161             shift = key["shift"];
14162             ctrl = key["ctrl"];
14163             alt = key["alt"];
14164         }else{
14165             keyCode = key;
14166         }
14167         var handler = function(dlg, e){
14168             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14169                 var k = e.getKey();
14170                 if(keyCode instanceof Array){
14171                     for(var i = 0, len = keyCode.length; i < len; i++){
14172                         if(keyCode[i] == k){
14173                           fn.call(scope || window, dlg, k, e);
14174                           return;
14175                         }
14176                     }
14177                 }else{
14178                     if(k == keyCode){
14179                         fn.call(scope || window, dlg, k, e);
14180                     }
14181                 }
14182             }
14183         };
14184         this.on("keydown", handler);
14185         return this;
14186     },
14187
14188     /**
14189      * Returns the TabPanel component (creates it if it doesn't exist).
14190      * Note: If you wish to simply check for the existence of tabs without creating them,
14191      * check for a null 'tabs' property.
14192      * @return {Roo.TabPanel} The tabs component
14193      */
14194     getTabs : function(){
14195         if(!this.tabs){
14196             this.el.addClass("x-dlg-auto-tabs");
14197             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14198             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14199         }
14200         return this.tabs;
14201     },
14202
14203     /**
14204      * Adds a button to the footer section of the dialog.
14205      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14206      * object or a valid Roo.DomHelper element config
14207      * @param {Function} handler The function called when the button is clicked
14208      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14209      * @return {Roo.Button} The new button
14210      */
14211     addButton : function(config, handler, scope){
14212         var dh = Roo.DomHelper;
14213         if(!this.footer){
14214             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14215         }
14216         if(!this.btnContainer){
14217             var tb = this.footer.createChild({
14218
14219                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14220                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14221             }, null, true);
14222             this.btnContainer = tb.firstChild.firstChild.firstChild;
14223         }
14224         var bconfig = {
14225             handler: handler,
14226             scope: scope,
14227             minWidth: this.minButtonWidth,
14228             hideParent:true
14229         };
14230         if(typeof config == "string"){
14231             bconfig.text = config;
14232         }else{
14233             if(config.tag){
14234                 bconfig.dhconfig = config;
14235             }else{
14236                 Roo.apply(bconfig, config);
14237             }
14238         }
14239         var fc = false;
14240         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14241             bconfig.position = Math.max(0, bconfig.position);
14242             fc = this.btnContainer.childNodes[bconfig.position];
14243         }
14244          
14245         var btn = new Roo.Button(
14246             fc ? 
14247                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14248                 : this.btnContainer.appendChild(document.createElement("td")),
14249             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14250             bconfig
14251         );
14252         this.syncBodyHeight();
14253         if(!this.buttons){
14254             /**
14255              * Array of all the buttons that have been added to this dialog via addButton
14256              * @type Array
14257              */
14258             this.buttons = [];
14259         }
14260         this.buttons.push(btn);
14261         return btn;
14262     },
14263
14264     /**
14265      * Sets the default button to be focused when the dialog is displayed.
14266      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14267      * @return {Roo.BasicDialog} this
14268      */
14269     setDefaultButton : function(btn){
14270         this.defaultButton = btn;
14271         return this;
14272     },
14273
14274     // private
14275     getHeaderFooterHeight : function(safe){
14276         var height = 0;
14277         if(this.header){
14278            height += this.header.getHeight();
14279         }
14280         if(this.footer){
14281            var fm = this.footer.getMargins();
14282             height += (this.footer.getHeight()+fm.top+fm.bottom);
14283         }
14284         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14285         height += this.centerBg.getPadding("tb");
14286         return height;
14287     },
14288
14289     // private
14290     syncBodyHeight : function()
14291     {
14292         var bd = this.body, // the text
14293             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14294             bw = this.bwrap;
14295         var height = this.size.height - this.getHeaderFooterHeight(false);
14296         bd.setHeight(height-bd.getMargins("tb"));
14297         var hh = this.header.getHeight();
14298         var h = this.size.height-hh;
14299         cb.setHeight(h);
14300         
14301         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14302         bw.setHeight(h-cb.getPadding("tb"));
14303         
14304         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14305         bd.setWidth(bw.getWidth(true));
14306         if(this.tabs){
14307             this.tabs.syncHeight();
14308             if(Roo.isIE){
14309                 this.tabs.el.repaint();
14310             }
14311         }
14312     },
14313
14314     /**
14315      * Restores the previous state of the dialog if Roo.state is configured.
14316      * @return {Roo.BasicDialog} this
14317      */
14318     restoreState : function(){
14319         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14320         if(box && box.width){
14321             this.xy = [box.x, box.y];
14322             this.resizeTo(box.width, box.height);
14323         }
14324         return this;
14325     },
14326
14327     // private
14328     beforeShow : function(){
14329         this.expand();
14330         if(this.fixedcenter){
14331             this.xy = this.el.getCenterXY(true);
14332         }
14333         if(this.modal){
14334             Roo.get(document.body).addClass("x-body-masked");
14335             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14336             this.mask.show();
14337         }
14338         this.constrainXY();
14339     },
14340
14341     // private
14342     animShow : function(){
14343         var b = Roo.get(this.animateTarget).getBox();
14344         this.proxy.setSize(b.width, b.height);
14345         this.proxy.setLocation(b.x, b.y);
14346         this.proxy.show();
14347         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14348                     true, .35, this.showEl.createDelegate(this));
14349     },
14350
14351     /**
14352      * Shows the dialog.
14353      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14354      * @return {Roo.BasicDialog} this
14355      */
14356     show : function(animateTarget){
14357         if (this.fireEvent("beforeshow", this) === false){
14358             return;
14359         }
14360         if(this.syncHeightBeforeShow){
14361             this.syncBodyHeight();
14362         }else if(this.firstShow){
14363             this.firstShow = false;
14364             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14365         }
14366         this.animateTarget = animateTarget || this.animateTarget;
14367         if(!this.el.isVisible()){
14368             this.beforeShow();
14369             if(this.animateTarget && Roo.get(this.animateTarget)){
14370                 this.animShow();
14371             }else{
14372                 this.showEl();
14373             }
14374         }
14375         return this;
14376     },
14377
14378     // private
14379     showEl : function(){
14380         this.proxy.hide();
14381         this.el.setXY(this.xy);
14382         this.el.show();
14383         this.adjustAssets(true);
14384         this.toFront();
14385         this.focus();
14386         // IE peekaboo bug - fix found by Dave Fenwick
14387         if(Roo.isIE){
14388             this.el.repaint();
14389         }
14390         this.fireEvent("show", this);
14391     },
14392
14393     /**
14394      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14395      * dialog itself will receive focus.
14396      */
14397     focus : function(){
14398         if(this.defaultButton){
14399             this.defaultButton.focus();
14400         }else{
14401             this.focusEl.focus();
14402         }
14403     },
14404
14405     // private
14406     constrainXY : function(){
14407         if(this.constraintoviewport !== false){
14408             if(!this.viewSize){
14409                 if(this.container){
14410                     var s = this.container.getSize();
14411                     this.viewSize = [s.width, s.height];
14412                 }else{
14413                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14414                 }
14415             }
14416             var s = Roo.get(this.container||document).getScroll();
14417
14418             var x = this.xy[0], y = this.xy[1];
14419             var w = this.size.width, h = this.size.height;
14420             var vw = this.viewSize[0], vh = this.viewSize[1];
14421             // only move it if it needs it
14422             var moved = false;
14423             // first validate right/bottom
14424             if(x + w > vw+s.left){
14425                 x = vw - w;
14426                 moved = true;
14427             }
14428             if(y + h > vh+s.top){
14429                 y = vh - h;
14430                 moved = true;
14431             }
14432             // then make sure top/left isn't negative
14433             if(x < s.left){
14434                 x = s.left;
14435                 moved = true;
14436             }
14437             if(y < s.top){
14438                 y = s.top;
14439                 moved = true;
14440             }
14441             if(moved){
14442                 // cache xy
14443                 this.xy = [x, y];
14444                 if(this.isVisible()){
14445                     this.el.setLocation(x, y);
14446                     this.adjustAssets();
14447                 }
14448             }
14449         }
14450     },
14451
14452     // private
14453     onDrag : function(){
14454         if(!this.proxyDrag){
14455             this.xy = this.el.getXY();
14456             this.adjustAssets();
14457         }
14458     },
14459
14460     // private
14461     adjustAssets : function(doShow){
14462         var x = this.xy[0], y = this.xy[1];
14463         var w = this.size.width, h = this.size.height;
14464         if(doShow === true){
14465             if(this.shadow){
14466                 this.shadow.show(this.el);
14467             }
14468             if(this.shim){
14469                 this.shim.show();
14470             }
14471         }
14472         if(this.shadow && this.shadow.isVisible()){
14473             this.shadow.show(this.el);
14474         }
14475         if(this.shim && this.shim.isVisible()){
14476             this.shim.setBounds(x, y, w, h);
14477         }
14478     },
14479
14480     // private
14481     adjustViewport : function(w, h){
14482         if(!w || !h){
14483             w = Roo.lib.Dom.getViewWidth();
14484             h = Roo.lib.Dom.getViewHeight();
14485         }
14486         // cache the size
14487         this.viewSize = [w, h];
14488         if(this.modal && this.mask.isVisible()){
14489             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14490             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14491         }
14492         if(this.isVisible()){
14493             this.constrainXY();
14494         }
14495     },
14496
14497     /**
14498      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14499      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14500      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14501      */
14502     destroy : function(removeEl){
14503         if(this.isVisible()){
14504             this.animateTarget = null;
14505             this.hide();
14506         }
14507         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14508         if(this.tabs){
14509             this.tabs.destroy(removeEl);
14510         }
14511         Roo.destroy(
14512              this.shim,
14513              this.proxy,
14514              this.resizer,
14515              this.close,
14516              this.mask
14517         );
14518         if(this.dd){
14519             this.dd.unreg();
14520         }
14521         if(this.buttons){
14522            for(var i = 0, len = this.buttons.length; i < len; i++){
14523                this.buttons[i].destroy();
14524            }
14525         }
14526         this.el.removeAllListeners();
14527         if(removeEl === true){
14528             this.el.update("");
14529             this.el.remove();
14530         }
14531         Roo.DialogManager.unregister(this);
14532     },
14533
14534     // private
14535     startMove : function(){
14536         if(this.proxyDrag){
14537             this.proxy.show();
14538         }
14539         if(this.constraintoviewport !== false){
14540             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14541         }
14542     },
14543
14544     // private
14545     endMove : function(){
14546         if(!this.proxyDrag){
14547             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14548         }else{
14549             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14550             this.proxy.hide();
14551         }
14552         this.refreshSize();
14553         this.adjustAssets();
14554         this.focus();
14555         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14556     },
14557
14558     /**
14559      * Brings this dialog to the front of any other visible dialogs
14560      * @return {Roo.BasicDialog} this
14561      */
14562     toFront : function(){
14563         Roo.DialogManager.bringToFront(this);
14564         return this;
14565     },
14566
14567     /**
14568      * Sends this dialog to the back (under) of any other visible dialogs
14569      * @return {Roo.BasicDialog} this
14570      */
14571     toBack : function(){
14572         Roo.DialogManager.sendToBack(this);
14573         return this;
14574     },
14575
14576     /**
14577      * Centers this dialog in the viewport
14578      * @return {Roo.BasicDialog} this
14579      */
14580     center : function(){
14581         var xy = this.el.getCenterXY(true);
14582         this.moveTo(xy[0], xy[1]);
14583         return this;
14584     },
14585
14586     /**
14587      * Moves the dialog's top-left corner to the specified point
14588      * @param {Number} x
14589      * @param {Number} y
14590      * @return {Roo.BasicDialog} this
14591      */
14592     moveTo : function(x, y){
14593         this.xy = [x,y];
14594         if(this.isVisible()){
14595             this.el.setXY(this.xy);
14596             this.adjustAssets();
14597         }
14598         return this;
14599     },
14600
14601     /**
14602      * Aligns the dialog to the specified element
14603      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14604      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14605      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14606      * @return {Roo.BasicDialog} this
14607      */
14608     alignTo : function(element, position, offsets){
14609         this.xy = this.el.getAlignToXY(element, position, offsets);
14610         if(this.isVisible()){
14611             this.el.setXY(this.xy);
14612             this.adjustAssets();
14613         }
14614         return this;
14615     },
14616
14617     /**
14618      * Anchors an element to another element and realigns it when the window is resized.
14619      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14620      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14621      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14622      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14623      * is a number, it is used as the buffer delay (defaults to 50ms).
14624      * @return {Roo.BasicDialog} this
14625      */
14626     anchorTo : function(el, alignment, offsets, monitorScroll){
14627         var action = function(){
14628             this.alignTo(el, alignment, offsets);
14629         };
14630         Roo.EventManager.onWindowResize(action, this);
14631         var tm = typeof monitorScroll;
14632         if(tm != 'undefined'){
14633             Roo.EventManager.on(window, 'scroll', action, this,
14634                 {buffer: tm == 'number' ? monitorScroll : 50});
14635         }
14636         action.call(this);
14637         return this;
14638     },
14639
14640     /**
14641      * Returns true if the dialog is visible
14642      * @return {Boolean}
14643      */
14644     isVisible : function(){
14645         return this.el.isVisible();
14646     },
14647
14648     // private
14649     animHide : function(callback){
14650         var b = Roo.get(this.animateTarget).getBox();
14651         this.proxy.show();
14652         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14653         this.el.hide();
14654         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14655                     this.hideEl.createDelegate(this, [callback]));
14656     },
14657
14658     /**
14659      * Hides the dialog.
14660      * @param {Function} callback (optional) Function to call when the dialog is hidden
14661      * @return {Roo.BasicDialog} this
14662      */
14663     hide : function(callback){
14664         if (this.fireEvent("beforehide", this) === false){
14665             return;
14666         }
14667         if(this.shadow){
14668             this.shadow.hide();
14669         }
14670         if(this.shim) {
14671           this.shim.hide();
14672         }
14673         // sometimes animateTarget seems to get set.. causing problems...
14674         // this just double checks..
14675         if(this.animateTarget && Roo.get(this.animateTarget)) {
14676            this.animHide(callback);
14677         }else{
14678             this.el.hide();
14679             this.hideEl(callback);
14680         }
14681         return this;
14682     },
14683
14684     // private
14685     hideEl : function(callback){
14686         this.proxy.hide();
14687         if(this.modal){
14688             this.mask.hide();
14689             Roo.get(document.body).removeClass("x-body-masked");
14690         }
14691         this.fireEvent("hide", this);
14692         if(typeof callback == "function"){
14693             callback();
14694         }
14695     },
14696
14697     // private
14698     hideAction : function(){
14699         this.setLeft("-10000px");
14700         this.setTop("-10000px");
14701         this.setStyle("visibility", "hidden");
14702     },
14703
14704     // private
14705     refreshSize : function(){
14706         this.size = this.el.getSize();
14707         this.xy = this.el.getXY();
14708         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14709     },
14710
14711     // private
14712     // z-index is managed by the DialogManager and may be overwritten at any time
14713     setZIndex : function(index){
14714         if(this.modal){
14715             this.mask.setStyle("z-index", index);
14716         }
14717         if(this.shim){
14718             this.shim.setStyle("z-index", ++index);
14719         }
14720         if(this.shadow){
14721             this.shadow.setZIndex(++index);
14722         }
14723         this.el.setStyle("z-index", ++index);
14724         if(this.proxy){
14725             this.proxy.setStyle("z-index", ++index);
14726         }
14727         if(this.resizer){
14728             this.resizer.proxy.setStyle("z-index", ++index);
14729         }
14730
14731         this.lastZIndex = index;
14732     },
14733
14734     /**
14735      * Returns the element for this dialog
14736      * @return {Roo.Element} The underlying dialog Element
14737      */
14738     getEl : function(){
14739         return this.el;
14740     }
14741 });
14742
14743 /**
14744  * @class Roo.DialogManager
14745  * Provides global access to BasicDialogs that have been created and
14746  * support for z-indexing (layering) multiple open dialogs.
14747  */
14748 Roo.DialogManager = function(){
14749     var list = {};
14750     var accessList = [];
14751     var front = null;
14752
14753     // private
14754     var sortDialogs = function(d1, d2){
14755         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14756     };
14757
14758     // private
14759     var orderDialogs = function(){
14760         accessList.sort(sortDialogs);
14761         var seed = Roo.DialogManager.zseed;
14762         for(var i = 0, len = accessList.length; i < len; i++){
14763             var dlg = accessList[i];
14764             if(dlg){
14765                 dlg.setZIndex(seed + (i*10));
14766             }
14767         }
14768     };
14769
14770     return {
14771         /**
14772          * The starting z-index for BasicDialogs (defaults to 9000)
14773          * @type Number The z-index value
14774          */
14775         zseed : 9000,
14776
14777         // private
14778         register : function(dlg){
14779             list[dlg.id] = dlg;
14780             accessList.push(dlg);
14781         },
14782
14783         // private
14784         unregister : function(dlg){
14785             delete list[dlg.id];
14786             var i=0;
14787             var len=0;
14788             if(!accessList.indexOf){
14789                 for(  i = 0, len = accessList.length; i < len; i++){
14790                     if(accessList[i] == dlg){
14791                         accessList.splice(i, 1);
14792                         return;
14793                     }
14794                 }
14795             }else{
14796                  i = accessList.indexOf(dlg);
14797                 if(i != -1){
14798                     accessList.splice(i, 1);
14799                 }
14800             }
14801         },
14802
14803         /**
14804          * Gets a registered dialog by id
14805          * @param {String/Object} id The id of the dialog or a dialog
14806          * @return {Roo.BasicDialog} this
14807          */
14808         get : function(id){
14809             return typeof id == "object" ? id : list[id];
14810         },
14811
14812         /**
14813          * Brings the specified dialog to the front
14814          * @param {String/Object} dlg The id of the dialog or a dialog
14815          * @return {Roo.BasicDialog} this
14816          */
14817         bringToFront : function(dlg){
14818             dlg = this.get(dlg);
14819             if(dlg != front){
14820                 front = dlg;
14821                 dlg._lastAccess = new Date().getTime();
14822                 orderDialogs();
14823             }
14824             return dlg;
14825         },
14826
14827         /**
14828          * Sends the specified dialog to the back
14829          * @param {String/Object} dlg The id of the dialog or a dialog
14830          * @return {Roo.BasicDialog} this
14831          */
14832         sendToBack : function(dlg){
14833             dlg = this.get(dlg);
14834             dlg._lastAccess = -(new Date().getTime());
14835             orderDialogs();
14836             return dlg;
14837         },
14838
14839         /**
14840          * Hides all dialogs
14841          */
14842         hideAll : function(){
14843             for(var id in list){
14844                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14845                     list[id].hide();
14846                 }
14847             }
14848         }
14849     };
14850 }();
14851
14852 /**
14853  * @class Roo.LayoutDialog
14854  * @extends Roo.BasicDialog
14855  * Dialog which provides adjustments for working with a layout in a Dialog.
14856  * Add your necessary layout config options to the dialog's config.<br>
14857  * Example usage (including a nested layout):
14858  * <pre><code>
14859 if(!dialog){
14860     dialog = new Roo.LayoutDialog("download-dlg", {
14861         modal: true,
14862         width:600,
14863         height:450,
14864         shadow:true,
14865         minWidth:500,
14866         minHeight:350,
14867         autoTabs:true,
14868         proxyDrag:true,
14869         // layout config merges with the dialog config
14870         center:{
14871             tabPosition: "top",
14872             alwaysShowTabs: true
14873         }
14874     });
14875     dialog.addKeyListener(27, dialog.hide, dialog);
14876     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14877     dialog.addButton("Build It!", this.getDownload, this);
14878
14879     // we can even add nested layouts
14880     var innerLayout = new Roo.BorderLayout("dl-inner", {
14881         east: {
14882             initialSize: 200,
14883             autoScroll:true,
14884             split:true
14885         },
14886         center: {
14887             autoScroll:true
14888         }
14889     });
14890     innerLayout.beginUpdate();
14891     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14892     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14893     innerLayout.endUpdate(true);
14894
14895     var layout = dialog.getLayout();
14896     layout.beginUpdate();
14897     layout.add("center", new Roo.ContentPanel("standard-panel",
14898                         {title: "Download the Source", fitToFrame:true}));
14899     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14900                {title: "Build your own roo.js"}));
14901     layout.getRegion("center").showPanel(sp);
14902     layout.endUpdate();
14903 }
14904 </code></pre>
14905     * @constructor
14906     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14907     * @param {Object} config configuration options
14908   */
14909 Roo.LayoutDialog = function(el, cfg){
14910     
14911     var config=  cfg;
14912     if (typeof(cfg) == 'undefined') {
14913         config = Roo.apply({}, el);
14914         // not sure why we use documentElement here.. - it should always be body.
14915         // IE7 borks horribly if we use documentElement.
14916         // webkit also does not like documentElement - it creates a body element...
14917         el = Roo.get( document.body || document.documentElement ).createChild();
14918         //config.autoCreate = true;
14919     }
14920     
14921     
14922     config.autoTabs = false;
14923     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14924     this.body.setStyle({overflow:"hidden", position:"relative"});
14925     this.layout = new Roo.BorderLayout(this.body.dom, config);
14926     this.layout.monitorWindowResize = false;
14927     this.el.addClass("x-dlg-auto-layout");
14928     // fix case when center region overwrites center function
14929     this.center = Roo.BasicDialog.prototype.center;
14930     this.on("show", this.layout.layout, this.layout, true);
14931     if (config.items) {
14932         var xitems = config.items;
14933         delete config.items;
14934         Roo.each(xitems, this.addxtype, this);
14935     }
14936     
14937     
14938 };
14939 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14940     /**
14941      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14942      * @deprecated
14943      */
14944     endUpdate : function(){
14945         this.layout.endUpdate();
14946     },
14947
14948     /**
14949      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14950      *  @deprecated
14951      */
14952     beginUpdate : function(){
14953         this.layout.beginUpdate();
14954     },
14955
14956     /**
14957      * Get the BorderLayout for this dialog
14958      * @return {Roo.BorderLayout}
14959      */
14960     getLayout : function(){
14961         return this.layout;
14962     },
14963
14964     showEl : function(){
14965         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14966         if(Roo.isIE7){
14967             this.layout.layout();
14968         }
14969     },
14970
14971     // private
14972     // Use the syncHeightBeforeShow config option to control this automatically
14973     syncBodyHeight : function(){
14974         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14975         if(this.layout){this.layout.layout();}
14976     },
14977     
14978       /**
14979      * Add an xtype element (actually adds to the layout.)
14980      * @return {Object} xdata xtype object data.
14981      */
14982     
14983     addxtype : function(c) {
14984         return this.layout.addxtype(c);
14985     }
14986 });/*
14987  * Based on:
14988  * Ext JS Library 1.1.1
14989  * Copyright(c) 2006-2007, Ext JS, LLC.
14990  *
14991  * Originally Released Under LGPL - original licence link has changed is not relivant.
14992  *
14993  * Fork - LGPL
14994  * <script type="text/javascript">
14995  */
14996  
14997 /**
14998  * @class Roo.MessageBox
14999  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15000  * Example usage:
15001  *<pre><code>
15002 // Basic alert:
15003 Roo.Msg.alert('Status', 'Changes saved successfully.');
15004
15005 // Prompt for user data:
15006 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15007     if (btn == 'ok'){
15008         // process text value...
15009     }
15010 });
15011
15012 // Show a dialog using config options:
15013 Roo.Msg.show({
15014    title:'Save Changes?',
15015    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15016    buttons: Roo.Msg.YESNOCANCEL,
15017    fn: processResult,
15018    animEl: 'elId'
15019 });
15020 </code></pre>
15021  * @singleton
15022  */
15023 Roo.MessageBox = function(){
15024     var dlg, opt, mask, waitTimer;
15025     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15026     var buttons, activeTextEl, bwidth;
15027
15028     // private
15029     var handleButton = function(button){
15030         dlg.hide();
15031         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15032     };
15033
15034     // private
15035     var handleHide = function(){
15036         if(opt && opt.cls){
15037             dlg.el.removeClass(opt.cls);
15038         }
15039         if(waitTimer){
15040             Roo.TaskMgr.stop(waitTimer);
15041             waitTimer = null;
15042         }
15043     };
15044
15045     // private
15046     var updateButtons = function(b){
15047         var width = 0;
15048         if(!b){
15049             buttons["ok"].hide();
15050             buttons["cancel"].hide();
15051             buttons["yes"].hide();
15052             buttons["no"].hide();
15053             dlg.footer.dom.style.display = 'none';
15054             return width;
15055         }
15056         dlg.footer.dom.style.display = '';
15057         for(var k in buttons){
15058             if(typeof buttons[k] != "function"){
15059                 if(b[k]){
15060                     buttons[k].show();
15061                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15062                     width += buttons[k].el.getWidth()+15;
15063                 }else{
15064                     buttons[k].hide();
15065                 }
15066             }
15067         }
15068         return width;
15069     };
15070
15071     // private
15072     var handleEsc = function(d, k, e){
15073         if(opt && opt.closable !== false){
15074             dlg.hide();
15075         }
15076         if(e){
15077             e.stopEvent();
15078         }
15079     };
15080
15081     return {
15082         /**
15083          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15084          * @return {Roo.BasicDialog} The BasicDialog element
15085          */
15086         getDialog : function(){
15087            if(!dlg){
15088                 dlg = new Roo.BasicDialog("x-msg-box", {
15089                     autoCreate : true,
15090                     shadow: true,
15091                     draggable: true,
15092                     resizable:false,
15093                     constraintoviewport:false,
15094                     fixedcenter:true,
15095                     collapsible : false,
15096                     shim:true,
15097                     modal: true,
15098                     width:400, height:100,
15099                     buttonAlign:"center",
15100                     closeClick : function(){
15101                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15102                             handleButton("no");
15103                         }else{
15104                             handleButton("cancel");
15105                         }
15106                     }
15107                 });
15108                 dlg.on("hide", handleHide);
15109                 mask = dlg.mask;
15110                 dlg.addKeyListener(27, handleEsc);
15111                 buttons = {};
15112                 var bt = this.buttonText;
15113                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15114                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15115                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15116                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15117                 bodyEl = dlg.body.createChild({
15118
15119                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15120                 });
15121                 msgEl = bodyEl.dom.firstChild;
15122                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15123                 textboxEl.enableDisplayMode();
15124                 textboxEl.addKeyListener([10,13], function(){
15125                     if(dlg.isVisible() && opt && opt.buttons){
15126                         if(opt.buttons.ok){
15127                             handleButton("ok");
15128                         }else if(opt.buttons.yes){
15129                             handleButton("yes");
15130                         }
15131                     }
15132                 });
15133                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15134                 textareaEl.enableDisplayMode();
15135                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15136                 progressEl.enableDisplayMode();
15137                 var pf = progressEl.dom.firstChild;
15138                 if (pf) {
15139                     pp = Roo.get(pf.firstChild);
15140                     pp.setHeight(pf.offsetHeight);
15141                 }
15142                 
15143             }
15144             return dlg;
15145         },
15146
15147         /**
15148          * Updates the message box body text
15149          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15150          * the XHTML-compliant non-breaking space character '&amp;#160;')
15151          * @return {Roo.MessageBox} This message box
15152          */
15153         updateText : function(text){
15154             if(!dlg.isVisible() && !opt.width){
15155                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15156             }
15157             msgEl.innerHTML = text || '&#160;';
15158       
15159             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15160             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15161             var w = Math.max(
15162                     Math.min(opt.width || cw , this.maxWidth), 
15163                     Math.max(opt.minWidth || this.minWidth, bwidth)
15164             );
15165             if(opt.prompt){
15166                 activeTextEl.setWidth(w);
15167             }
15168             if(dlg.isVisible()){
15169                 dlg.fixedcenter = false;
15170             }
15171             // to big, make it scroll. = But as usual stupid IE does not support
15172             // !important..
15173             
15174             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15175                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15176                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15177             } else {
15178                 bodyEl.dom.style.height = '';
15179                 bodyEl.dom.style.overflowY = '';
15180             }
15181             if (cw > w) {
15182                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15183             } else {
15184                 bodyEl.dom.style.overflowX = '';
15185             }
15186             
15187             dlg.setContentSize(w, bodyEl.getHeight());
15188             if(dlg.isVisible()){
15189                 dlg.fixedcenter = true;
15190             }
15191             return this;
15192         },
15193
15194         /**
15195          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15196          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15197          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15198          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15199          * @return {Roo.MessageBox} This message box
15200          */
15201         updateProgress : function(value, text){
15202             if(text){
15203                 this.updateText(text);
15204             }
15205             if (pp) { // weird bug on my firefox - for some reason this is not defined
15206                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15207             }
15208             return this;
15209         },        
15210
15211         /**
15212          * Returns true if the message box is currently displayed
15213          * @return {Boolean} True if the message box is visible, else false
15214          */
15215         isVisible : function(){
15216             return dlg && dlg.isVisible();  
15217         },
15218
15219         /**
15220          * Hides the message box if it is displayed
15221          */
15222         hide : function(){
15223             if(this.isVisible()){
15224                 dlg.hide();
15225             }  
15226         },
15227
15228         /**
15229          * Displays a new message box, or reinitializes an existing message box, based on the config options
15230          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15231          * The following config object properties are supported:
15232          * <pre>
15233 Property    Type             Description
15234 ----------  ---------------  ------------------------------------------------------------------------------------
15235 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15236                                    closes (defaults to undefined)
15237 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15238                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15239 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15240                                    progress and wait dialogs will ignore this property and always hide the
15241                                    close button as they can only be closed programmatically.
15242 cls               String           A custom CSS class to apply to the message box element
15243 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15244                                    displayed (defaults to 75)
15245 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15246                                    function will be btn (the name of the button that was clicked, if applicable,
15247                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15248                                    Progress and wait dialogs will ignore this option since they do not respond to
15249                                    user actions and can only be closed programmatically, so any required function
15250                                    should be called by the same code after it closes the dialog.
15251 icon              String           A CSS class that provides a background image to be used as an icon for
15252                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15253 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15254 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15255 modal             Boolean          False to allow user interaction with the page while the message box is
15256                                    displayed (defaults to true)
15257 msg               String           A string that will replace the existing message box body text (defaults
15258                                    to the XHTML-compliant non-breaking space character '&#160;')
15259 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15260 progress          Boolean          True to display a progress bar (defaults to false)
15261 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15262 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15263 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15264 title             String           The title text
15265 value             String           The string value to set into the active textbox element if displayed
15266 wait              Boolean          True to display a progress bar (defaults to false)
15267 width             Number           The width of the dialog in pixels
15268 </pre>
15269          *
15270          * Example usage:
15271          * <pre><code>
15272 Roo.Msg.show({
15273    title: 'Address',
15274    msg: 'Please enter your address:',
15275    width: 300,
15276    buttons: Roo.MessageBox.OKCANCEL,
15277    multiline: true,
15278    fn: saveAddress,
15279    animEl: 'addAddressBtn'
15280 });
15281 </code></pre>
15282          * @param {Object} config Configuration options
15283          * @return {Roo.MessageBox} This message box
15284          */
15285         show : function(options)
15286         {
15287             
15288             // this causes nightmares if you show one dialog after another
15289             // especially on callbacks..
15290              
15291             if(this.isVisible()){
15292                 
15293                 this.hide();
15294                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15295                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15296                 Roo.log("New Dialog Message:" +  options.msg )
15297                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15298                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15299                 
15300             }
15301             var d = this.getDialog();
15302             opt = options;
15303             d.setTitle(opt.title || "&#160;");
15304             d.close.setDisplayed(opt.closable !== false);
15305             activeTextEl = textboxEl;
15306             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15307             if(opt.prompt){
15308                 if(opt.multiline){
15309                     textboxEl.hide();
15310                     textareaEl.show();
15311                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15312                         opt.multiline : this.defaultTextHeight);
15313                     activeTextEl = textareaEl;
15314                 }else{
15315                     textboxEl.show();
15316                     textareaEl.hide();
15317                 }
15318             }else{
15319                 textboxEl.hide();
15320                 textareaEl.hide();
15321             }
15322             progressEl.setDisplayed(opt.progress === true);
15323             this.updateProgress(0);
15324             activeTextEl.dom.value = opt.value || "";
15325             if(opt.prompt){
15326                 dlg.setDefaultButton(activeTextEl);
15327             }else{
15328                 var bs = opt.buttons;
15329                 var db = null;
15330                 if(bs && bs.ok){
15331                     db = buttons["ok"];
15332                 }else if(bs && bs.yes){
15333                     db = buttons["yes"];
15334                 }
15335                 dlg.setDefaultButton(db);
15336             }
15337             bwidth = updateButtons(opt.buttons);
15338             this.updateText(opt.msg);
15339             if(opt.cls){
15340                 d.el.addClass(opt.cls);
15341             }
15342             d.proxyDrag = opt.proxyDrag === true;
15343             d.modal = opt.modal !== false;
15344             d.mask = opt.modal !== false ? mask : false;
15345             if(!d.isVisible()){
15346                 // force it to the end of the z-index stack so it gets a cursor in FF
15347                 document.body.appendChild(dlg.el.dom);
15348                 d.animateTarget = null;
15349                 d.show(options.animEl);
15350             }
15351             return this;
15352         },
15353
15354         /**
15355          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15356          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15357          * and closing the message box when the process is complete.
15358          * @param {String} title The title bar text
15359          * @param {String} msg The message box body text
15360          * @return {Roo.MessageBox} This message box
15361          */
15362         progress : function(title, msg){
15363             this.show({
15364                 title : title,
15365                 msg : msg,
15366                 buttons: false,
15367                 progress:true,
15368                 closable:false,
15369                 minWidth: this.minProgressWidth,
15370                 modal : true
15371             });
15372             return this;
15373         },
15374
15375         /**
15376          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15377          * If a callback function is passed it will be called after the user clicks the button, and the
15378          * id of the button that was clicked will be passed as the only parameter to the callback
15379          * (could also be the top-right close button).
15380          * @param {String} title The title bar text
15381          * @param {String} msg The message box body text
15382          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15383          * @param {Object} scope (optional) The scope of the callback function
15384          * @return {Roo.MessageBox} This message box
15385          */
15386         alert : function(title, msg, fn, scope){
15387             this.show({
15388                 title : title,
15389                 msg : msg,
15390                 buttons: this.OK,
15391                 fn: fn,
15392                 scope : scope,
15393                 modal : true
15394             });
15395             return this;
15396         },
15397
15398         /**
15399          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15400          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15401          * You are responsible for closing the message box when the process is complete.
15402          * @param {String} msg The message box body text
15403          * @param {String} title (optional) The title bar text
15404          * @return {Roo.MessageBox} This message box
15405          */
15406         wait : function(msg, title){
15407             this.show({
15408                 title : title,
15409                 msg : msg,
15410                 buttons: false,
15411                 closable:false,
15412                 progress:true,
15413                 modal:true,
15414                 width:300,
15415                 wait:true
15416             });
15417             waitTimer = Roo.TaskMgr.start({
15418                 run: function(i){
15419                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15420                 },
15421                 interval: 1000
15422             });
15423             return this;
15424         },
15425
15426         /**
15427          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15428          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15429          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15430          * @param {String} title The title bar text
15431          * @param {String} msg The message box body text
15432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15433          * @param {Object} scope (optional) The scope of the callback function
15434          * @return {Roo.MessageBox} This message box
15435          */
15436         confirm : function(title, msg, fn, scope){
15437             this.show({
15438                 title : title,
15439                 msg : msg,
15440                 buttons: this.YESNO,
15441                 fn: fn,
15442                 scope : scope,
15443                 modal : true
15444             });
15445             return this;
15446         },
15447
15448         /**
15449          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15450          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15451          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15452          * (could also be the top-right close button) and the text that was entered will be passed as the two
15453          * parameters to the callback.
15454          * @param {String} title The title bar text
15455          * @param {String} msg The message box body text
15456          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15457          * @param {Object} scope (optional) The scope of the callback function
15458          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15459          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15460          * @return {Roo.MessageBox} This message box
15461          */
15462         prompt : function(title, msg, fn, scope, multiline){
15463             this.show({
15464                 title : title,
15465                 msg : msg,
15466                 buttons: this.OKCANCEL,
15467                 fn: fn,
15468                 minWidth:250,
15469                 scope : scope,
15470                 prompt:true,
15471                 multiline: multiline,
15472                 modal : true
15473             });
15474             return this;
15475         },
15476
15477         /**
15478          * Button config that displays a single OK button
15479          * @type Object
15480          */
15481         OK : {ok:true},
15482         /**
15483          * Button config that displays Yes and No buttons
15484          * @type Object
15485          */
15486         YESNO : {yes:true, no:true},
15487         /**
15488          * Button config that displays OK and Cancel buttons
15489          * @type Object
15490          */
15491         OKCANCEL : {ok:true, cancel:true},
15492         /**
15493          * Button config that displays Yes, No and Cancel buttons
15494          * @type Object
15495          */
15496         YESNOCANCEL : {yes:true, no:true, cancel:true},
15497
15498         /**
15499          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15500          * @type Number
15501          */
15502         defaultTextHeight : 75,
15503         /**
15504          * The maximum width in pixels of the message box (defaults to 600)
15505          * @type Number
15506          */
15507         maxWidth : 600,
15508         /**
15509          * The minimum width in pixels of the message box (defaults to 100)
15510          * @type Number
15511          */
15512         minWidth : 100,
15513         /**
15514          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15515          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15516          * @type Number
15517          */
15518         minProgressWidth : 250,
15519         /**
15520          * An object containing the default button text strings that can be overriden for localized language support.
15521          * Supported properties are: ok, cancel, yes and no.
15522          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15523          * @type Object
15524          */
15525         buttonText : {
15526             ok : "OK",
15527             cancel : "Cancel",
15528             yes : "Yes",
15529             no : "No"
15530         }
15531     };
15532 }();
15533
15534 /**
15535  * Shorthand for {@link Roo.MessageBox}
15536  */
15537 Roo.Msg = Roo.MessageBox;/*
15538  * Based on:
15539  * Ext JS Library 1.1.1
15540  * Copyright(c) 2006-2007, Ext JS, LLC.
15541  *
15542  * Originally Released Under LGPL - original licence link has changed is not relivant.
15543  *
15544  * Fork - LGPL
15545  * <script type="text/javascript">
15546  */
15547 /**
15548  * @class Roo.QuickTips
15549  * Provides attractive and customizable tooltips for any element.
15550  * @singleton
15551  */
15552 Roo.QuickTips = function(){
15553     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15554     var ce, bd, xy, dd;
15555     var visible = false, disabled = true, inited = false;
15556     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15557     
15558     var onOver = function(e){
15559         if(disabled){
15560             return;
15561         }
15562         var t = e.getTarget();
15563         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15564             return;
15565         }
15566         if(ce && t == ce.el){
15567             clearTimeout(hideProc);
15568             return;
15569         }
15570         if(t && tagEls[t.id]){
15571             tagEls[t.id].el = t;
15572             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15573             return;
15574         }
15575         var ttp, et = Roo.fly(t);
15576         var ns = cfg.namespace;
15577         if(tm.interceptTitles && t.title){
15578             ttp = t.title;
15579             t.qtip = ttp;
15580             t.removeAttribute("title");
15581             e.preventDefault();
15582         }else{
15583             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
15584         }
15585         if(ttp){
15586             showProc = show.defer(tm.showDelay, tm, [{
15587                 el: t, 
15588                 text: ttp, 
15589                 width: et.getAttributeNS(ns, cfg.width),
15590                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15591                 title: et.getAttributeNS(ns, cfg.title),
15592                     cls: et.getAttributeNS(ns, cfg.cls)
15593             }]);
15594         }
15595     };
15596     
15597     var onOut = function(e){
15598         clearTimeout(showProc);
15599         var t = e.getTarget();
15600         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15601             hideProc = setTimeout(hide, tm.hideDelay);
15602         }
15603     };
15604     
15605     var onMove = function(e){
15606         if(disabled){
15607             return;
15608         }
15609         xy = e.getXY();
15610         xy[1] += 18;
15611         if(tm.trackMouse && ce){
15612             el.setXY(xy);
15613         }
15614     };
15615     
15616     var onDown = function(e){
15617         clearTimeout(showProc);
15618         clearTimeout(hideProc);
15619         if(!e.within(el)){
15620             if(tm.hideOnClick){
15621                 hide();
15622                 tm.disable();
15623                 tm.enable.defer(100, tm);
15624             }
15625         }
15626     };
15627     
15628     var getPad = function(){
15629         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15630     };
15631
15632     var show = function(o){
15633         if(disabled){
15634             return;
15635         }
15636         clearTimeout(dismissProc);
15637         ce = o;
15638         if(removeCls){ // in case manually hidden
15639             el.removeClass(removeCls);
15640             removeCls = null;
15641         }
15642         if(ce.cls){
15643             el.addClass(ce.cls);
15644             removeCls = ce.cls;
15645         }
15646         if(ce.title){
15647             tipTitle.update(ce.title);
15648             tipTitle.show();
15649         }else{
15650             tipTitle.update('');
15651             tipTitle.hide();
15652         }
15653         el.dom.style.width  = tm.maxWidth+'px';
15654         //tipBody.dom.style.width = '';
15655         tipBodyText.update(o.text);
15656         var p = getPad(), w = ce.width;
15657         if(!w){
15658             var td = tipBodyText.dom;
15659             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15660             if(aw > tm.maxWidth){
15661                 w = tm.maxWidth;
15662             }else if(aw < tm.minWidth){
15663                 w = tm.minWidth;
15664             }else{
15665                 w = aw;
15666             }
15667         }
15668         //tipBody.setWidth(w);
15669         el.setWidth(parseInt(w, 10) + p);
15670         if(ce.autoHide === false){
15671             close.setDisplayed(true);
15672             if(dd){
15673                 dd.unlock();
15674             }
15675         }else{
15676             close.setDisplayed(false);
15677             if(dd){
15678                 dd.lock();
15679             }
15680         }
15681         if(xy){
15682             el.avoidY = xy[1]-18;
15683             el.setXY(xy);
15684         }
15685         if(tm.animate){
15686             el.setOpacity(.1);
15687             el.setStyle("visibility", "visible");
15688             el.fadeIn({callback: afterShow});
15689         }else{
15690             afterShow();
15691         }
15692     };
15693     
15694     var afterShow = function(){
15695         if(ce){
15696             el.show();
15697             esc.enable();
15698             if(tm.autoDismiss && ce.autoHide !== false){
15699                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15700             }
15701         }
15702     };
15703     
15704     var hide = function(noanim){
15705         clearTimeout(dismissProc);
15706         clearTimeout(hideProc);
15707         ce = null;
15708         if(el.isVisible()){
15709             esc.disable();
15710             if(noanim !== true && tm.animate){
15711                 el.fadeOut({callback: afterHide});
15712             }else{
15713                 afterHide();
15714             } 
15715         }
15716     };
15717     
15718     var afterHide = function(){
15719         el.hide();
15720         if(removeCls){
15721             el.removeClass(removeCls);
15722             removeCls = null;
15723         }
15724     };
15725     
15726     return {
15727         /**
15728         * @cfg {Number} minWidth
15729         * The minimum width of the quick tip (defaults to 40)
15730         */
15731        minWidth : 40,
15732         /**
15733         * @cfg {Number} maxWidth
15734         * The maximum width of the quick tip (defaults to 300)
15735         */
15736        maxWidth : 300,
15737         /**
15738         * @cfg {Boolean} interceptTitles
15739         * True to automatically use the element's DOM title value if available (defaults to false)
15740         */
15741        interceptTitles : false,
15742         /**
15743         * @cfg {Boolean} trackMouse
15744         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15745         */
15746        trackMouse : false,
15747         /**
15748         * @cfg {Boolean} hideOnClick
15749         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15750         */
15751        hideOnClick : true,
15752         /**
15753         * @cfg {Number} showDelay
15754         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15755         */
15756        showDelay : 500,
15757         /**
15758         * @cfg {Number} hideDelay
15759         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15760         */
15761        hideDelay : 200,
15762         /**
15763         * @cfg {Boolean} autoHide
15764         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15765         * Used in conjunction with hideDelay.
15766         */
15767        autoHide : true,
15768         /**
15769         * @cfg {Boolean}
15770         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15771         * (defaults to true).  Used in conjunction with autoDismissDelay.
15772         */
15773        autoDismiss : true,
15774         /**
15775         * @cfg {Number}
15776         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15777         */
15778        autoDismissDelay : 5000,
15779        /**
15780         * @cfg {Boolean} animate
15781         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15782         */
15783        animate : false,
15784
15785        /**
15786         * @cfg {String} title
15787         * Title text to display (defaults to '').  This can be any valid HTML markup.
15788         */
15789         title: '',
15790        /**
15791         * @cfg {String} text
15792         * Body text to display (defaults to '').  This can be any valid HTML markup.
15793         */
15794         text : '',
15795        /**
15796         * @cfg {String} cls
15797         * A CSS class to apply to the base quick tip element (defaults to '').
15798         */
15799         cls : '',
15800        /**
15801         * @cfg {Number} width
15802         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15803         * minWidth or maxWidth.
15804         */
15805         width : null,
15806
15807     /**
15808      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15809      * or display QuickTips in a page.
15810      */
15811        init : function(){
15812           tm = Roo.QuickTips;
15813           cfg = tm.tagConfig;
15814           if(!inited){
15815               if(!Roo.isReady){ // allow calling of init() before onReady
15816                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15817                   return;
15818               }
15819               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15820               el.fxDefaults = {stopFx: true};
15821               // maximum custom styling
15822               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
15823               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
15824               tipTitle = el.child('h3');
15825               tipTitle.enableDisplayMode("block");
15826               tipBody = el.child('div.x-tip-bd');
15827               tipBodyText = el.child('div.x-tip-bd-inner');
15828               //bdLeft = el.child('div.x-tip-bd-left');
15829               //bdRight = el.child('div.x-tip-bd-right');
15830               close = el.child('div.x-tip-close');
15831               close.enableDisplayMode("block");
15832               close.on("click", hide);
15833               var d = Roo.get(document);
15834               d.on("mousedown", onDown);
15835               d.on("mouseover", onOver);
15836               d.on("mouseout", onOut);
15837               d.on("mousemove", onMove);
15838               esc = d.addKeyListener(27, hide);
15839               esc.disable();
15840               if(Roo.dd.DD){
15841                   dd = el.initDD("default", null, {
15842                       onDrag : function(){
15843                           el.sync();  
15844                       }
15845                   });
15846                   dd.setHandleElId(tipTitle.id);
15847                   dd.lock();
15848               }
15849               inited = true;
15850           }
15851           this.enable(); 
15852        },
15853
15854     /**
15855      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15856      * are supported:
15857      * <pre>
15858 Property    Type                   Description
15859 ----------  ---------------------  ------------------------------------------------------------------------
15860 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15861      * </ul>
15862      * @param {Object} config The config object
15863      */
15864        register : function(config){
15865            var cs = config instanceof Array ? config : arguments;
15866            for(var i = 0, len = cs.length; i < len; i++) {
15867                var c = cs[i];
15868                var target = c.target;
15869                if(target){
15870                    if(target instanceof Array){
15871                        for(var j = 0, jlen = target.length; j < jlen; j++){
15872                            tagEls[target[j]] = c;
15873                        }
15874                    }else{
15875                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15876                    }
15877                }
15878            }
15879        },
15880
15881     /**
15882      * Removes this quick tip from its element and destroys it.
15883      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15884      */
15885        unregister : function(el){
15886            delete tagEls[Roo.id(el)];
15887        },
15888
15889     /**
15890      * Enable this quick tip.
15891      */
15892        enable : function(){
15893            if(inited && disabled){
15894                locks.pop();
15895                if(locks.length < 1){
15896                    disabled = false;
15897                }
15898            }
15899        },
15900
15901     /**
15902      * Disable this quick tip.
15903      */
15904        disable : function(){
15905           disabled = true;
15906           clearTimeout(showProc);
15907           clearTimeout(hideProc);
15908           clearTimeout(dismissProc);
15909           if(ce){
15910               hide(true);
15911           }
15912           locks.push(1);
15913        },
15914
15915     /**
15916      * Returns true if the quick tip is enabled, else false.
15917      */
15918        isEnabled : function(){
15919             return !disabled;
15920        },
15921
15922         // private
15923        tagConfig : {
15924            namespace : "roo", // was ext?? this may break..
15925            alt_namespace : "ext",
15926            attribute : "qtip",
15927            width : "width",
15928            target : "target",
15929            title : "qtitle",
15930            hide : "hide",
15931            cls : "qclass"
15932        }
15933    };
15934 }();
15935
15936 // backwards compat
15937 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15938  * Based on:
15939  * Ext JS Library 1.1.1
15940  * Copyright(c) 2006-2007, Ext JS, LLC.
15941  *
15942  * Originally Released Under LGPL - original licence link has changed is not relivant.
15943  *
15944  * Fork - LGPL
15945  * <script type="text/javascript">
15946  */
15947  
15948
15949 /**
15950  * @class Roo.tree.TreePanel
15951  * @extends Roo.data.Tree
15952
15953  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15954  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15955  * @cfg {Boolean} enableDD true to enable drag and drop
15956  * @cfg {Boolean} enableDrag true to enable just drag
15957  * @cfg {Boolean} enableDrop true to enable just drop
15958  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15959  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15960  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15961  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15962  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15963  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15964  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15965  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15966  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15967  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15968  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15969  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15970  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15971  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15972  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
15973  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
15974  * 
15975  * @constructor
15976  * @param {String/HTMLElement/Element} el The container element
15977  * @param {Object} config
15978  */
15979 Roo.tree.TreePanel = function(el, config){
15980     var root = false;
15981     var loader = false;
15982     if (config.root) {
15983         root = config.root;
15984         delete config.root;
15985     }
15986     if (config.loader) {
15987         loader = config.loader;
15988         delete config.loader;
15989     }
15990     
15991     Roo.apply(this, config);
15992     Roo.tree.TreePanel.superclass.constructor.call(this);
15993     this.el = Roo.get(el);
15994     this.el.addClass('x-tree');
15995     //console.log(root);
15996     if (root) {
15997         this.setRootNode( Roo.factory(root, Roo.tree));
15998     }
15999     if (loader) {
16000         this.loader = Roo.factory(loader, Roo.tree);
16001     }
16002    /**
16003     * Read-only. The id of the container element becomes this TreePanel's id.
16004     */
16005     this.id = this.el.id;
16006     this.addEvents({
16007         /**
16008         * @event beforeload
16009         * Fires before a node is loaded, return false to cancel
16010         * @param {Node} node The node being loaded
16011         */
16012         "beforeload" : true,
16013         /**
16014         * @event load
16015         * Fires when a node is loaded
16016         * @param {Node} node The node that was loaded
16017         */
16018         "load" : true,
16019         /**
16020         * @event textchange
16021         * Fires when the text for a node is changed
16022         * @param {Node} node The node
16023         * @param {String} text The new text
16024         * @param {String} oldText The old text
16025         */
16026         "textchange" : true,
16027         /**
16028         * @event beforeexpand
16029         * Fires before a node is expanded, return false to cancel.
16030         * @param {Node} node The node
16031         * @param {Boolean} deep
16032         * @param {Boolean} anim
16033         */
16034         "beforeexpand" : true,
16035         /**
16036         * @event beforecollapse
16037         * Fires before a node is collapsed, return false to cancel.
16038         * @param {Node} node The node
16039         * @param {Boolean} deep
16040         * @param {Boolean} anim
16041         */
16042         "beforecollapse" : true,
16043         /**
16044         * @event expand
16045         * Fires when a node is expanded
16046         * @param {Node} node The node
16047         */
16048         "expand" : true,
16049         /**
16050         * @event disabledchange
16051         * Fires when the disabled status of a node changes
16052         * @param {Node} node The node
16053         * @param {Boolean} disabled
16054         */
16055         "disabledchange" : true,
16056         /**
16057         * @event collapse
16058         * Fires when a node is collapsed
16059         * @param {Node} node The node
16060         */
16061         "collapse" : true,
16062         /**
16063         * @event beforeclick
16064         * Fires before click processing on a node. Return false to cancel the default action.
16065         * @param {Node} node The node
16066         * @param {Roo.EventObject} e The event object
16067         */
16068         "beforeclick":true,
16069         /**
16070         * @event checkchange
16071         * Fires when a node with a checkbox's checked property changes
16072         * @param {Node} this This node
16073         * @param {Boolean} checked
16074         */
16075         "checkchange":true,
16076         /**
16077         * @event click
16078         * Fires when a node is clicked
16079         * @param {Node} node The node
16080         * @param {Roo.EventObject} e The event object
16081         */
16082         "click":true,
16083         /**
16084         * @event dblclick
16085         * Fires when a node is double clicked
16086         * @param {Node} node The node
16087         * @param {Roo.EventObject} e The event object
16088         */
16089         "dblclick":true,
16090         /**
16091         * @event contextmenu
16092         * Fires when a node is right clicked
16093         * @param {Node} node The node
16094         * @param {Roo.EventObject} e The event object
16095         */
16096         "contextmenu":true,
16097         /**
16098         * @event beforechildrenrendered
16099         * Fires right before the child nodes for a node are rendered
16100         * @param {Node} node The node
16101         */
16102         "beforechildrenrendered":true,
16103         /**
16104         * @event startdrag
16105         * Fires when a node starts being dragged
16106         * @param {Roo.tree.TreePanel} this
16107         * @param {Roo.tree.TreeNode} node
16108         * @param {event} e The raw browser event
16109         */ 
16110        "startdrag" : true,
16111        /**
16112         * @event enddrag
16113         * Fires when a drag operation is complete
16114         * @param {Roo.tree.TreePanel} this
16115         * @param {Roo.tree.TreeNode} node
16116         * @param {event} e The raw browser event
16117         */
16118        "enddrag" : true,
16119        /**
16120         * @event dragdrop
16121         * Fires when a dragged node is dropped on a valid DD target
16122         * @param {Roo.tree.TreePanel} this
16123         * @param {Roo.tree.TreeNode} node
16124         * @param {DD} dd The dd it was dropped on
16125         * @param {event} e The raw browser event
16126         */
16127        "dragdrop" : true,
16128        /**
16129         * @event beforenodedrop
16130         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16131         * passed to handlers has the following properties:<br />
16132         * <ul style="padding:5px;padding-left:16px;">
16133         * <li>tree - The TreePanel</li>
16134         * <li>target - The node being targeted for the drop</li>
16135         * <li>data - The drag data from the drag source</li>
16136         * <li>point - The point of the drop - append, above or below</li>
16137         * <li>source - The drag source</li>
16138         * <li>rawEvent - Raw mouse event</li>
16139         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16140         * to be inserted by setting them on this object.</li>
16141         * <li>cancel - Set this to true to cancel the drop.</li>
16142         * </ul>
16143         * @param {Object} dropEvent
16144         */
16145        "beforenodedrop" : true,
16146        /**
16147         * @event nodedrop
16148         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16149         * passed to handlers has the following properties:<br />
16150         * <ul style="padding:5px;padding-left:16px;">
16151         * <li>tree - The TreePanel</li>
16152         * <li>target - The node being targeted for the drop</li>
16153         * <li>data - The drag data from the drag source</li>
16154         * <li>point - The point of the drop - append, above or below</li>
16155         * <li>source - The drag source</li>
16156         * <li>rawEvent - Raw mouse event</li>
16157         * <li>dropNode - Dropped node(s).</li>
16158         * </ul>
16159         * @param {Object} dropEvent
16160         */
16161        "nodedrop" : true,
16162         /**
16163         * @event nodedragover
16164         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16165         * passed to handlers has the following properties:<br />
16166         * <ul style="padding:5px;padding-left:16px;">
16167         * <li>tree - The TreePanel</li>
16168         * <li>target - The node being targeted for the drop</li>
16169         * <li>data - The drag data from the drag source</li>
16170         * <li>point - The point of the drop - append, above or below</li>
16171         * <li>source - The drag source</li>
16172         * <li>rawEvent - Raw mouse event</li>
16173         * <li>dropNode - Drop node(s) provided by the source.</li>
16174         * <li>cancel - Set this to true to signal drop not allowed.</li>
16175         * </ul>
16176         * @param {Object} dragOverEvent
16177         */
16178        "nodedragover" : true
16179         
16180     });
16181     if(this.singleExpand){
16182        this.on("beforeexpand", this.restrictExpand, this);
16183     }
16184     if (this.editor) {
16185         this.editor.tree = this;
16186         this.editor = Roo.factory(this.editor, Roo.tree);
16187     }
16188     
16189     if (this.selModel) {
16190         this.selModel = Roo.factory(this.selModel, Roo.tree);
16191     }
16192    
16193 };
16194 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16195     rootVisible : true,
16196     animate: Roo.enableFx,
16197     lines : true,
16198     enableDD : false,
16199     hlDrop : Roo.enableFx,
16200   
16201     renderer: false,
16202     
16203     rendererTip: false,
16204     // private
16205     restrictExpand : function(node){
16206         var p = node.parentNode;
16207         if(p){
16208             if(p.expandedChild && p.expandedChild.parentNode == p){
16209                 p.expandedChild.collapse();
16210             }
16211             p.expandedChild = node;
16212         }
16213     },
16214
16215     // private override
16216     setRootNode : function(node){
16217         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16218         if(!this.rootVisible){
16219             node.ui = new Roo.tree.RootTreeNodeUI(node);
16220         }
16221         return node;
16222     },
16223
16224     /**
16225      * Returns the container element for this TreePanel
16226      */
16227     getEl : function(){
16228         return this.el;
16229     },
16230
16231     /**
16232      * Returns the default TreeLoader for this TreePanel
16233      */
16234     getLoader : function(){
16235         return this.loader;
16236     },
16237
16238     /**
16239      * Expand all nodes
16240      */
16241     expandAll : function(){
16242         this.root.expand(true);
16243     },
16244
16245     /**
16246      * Collapse all nodes
16247      */
16248     collapseAll : function(){
16249         this.root.collapse(true);
16250     },
16251
16252     /**
16253      * Returns the selection model used by this TreePanel
16254      */
16255     getSelectionModel : function(){
16256         if(!this.selModel){
16257             this.selModel = new Roo.tree.DefaultSelectionModel();
16258         }
16259         return this.selModel;
16260     },
16261
16262     /**
16263      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16264      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16265      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16266      * @return {Array}
16267      */
16268     getChecked : function(a, startNode){
16269         startNode = startNode || this.root;
16270         var r = [];
16271         var f = function(){
16272             if(this.attributes.checked){
16273                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16274             }
16275         }
16276         startNode.cascade(f);
16277         return r;
16278     },
16279
16280     /**
16281      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16282      * @param {String} path
16283      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16284      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16285      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16286      */
16287     expandPath : function(path, attr, callback){
16288         attr = attr || "id";
16289         var keys = path.split(this.pathSeparator);
16290         var curNode = this.root;
16291         if(curNode.attributes[attr] != keys[1]){ // invalid root
16292             if(callback){
16293                 callback(false, null);
16294             }
16295             return;
16296         }
16297         var index = 1;
16298         var f = function(){
16299             if(++index == keys.length){
16300                 if(callback){
16301                     callback(true, curNode);
16302                 }
16303                 return;
16304             }
16305             var c = curNode.findChild(attr, keys[index]);
16306             if(!c){
16307                 if(callback){
16308                     callback(false, curNode);
16309                 }
16310                 return;
16311             }
16312             curNode = c;
16313             c.expand(false, false, f);
16314         };
16315         curNode.expand(false, false, f);
16316     },
16317
16318     /**
16319      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16320      * @param {String} path
16321      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16322      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16323      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16324      */
16325     selectPath : function(path, attr, callback){
16326         attr = attr || "id";
16327         var keys = path.split(this.pathSeparator);
16328         var v = keys.pop();
16329         if(keys.length > 0){
16330             var f = function(success, node){
16331                 if(success && node){
16332                     var n = node.findChild(attr, v);
16333                     if(n){
16334                         n.select();
16335                         if(callback){
16336                             callback(true, n);
16337                         }
16338                     }else if(callback){
16339                         callback(false, n);
16340                     }
16341                 }else{
16342                     if(callback){
16343                         callback(false, n);
16344                     }
16345                 }
16346             };
16347             this.expandPath(keys.join(this.pathSeparator), attr, f);
16348         }else{
16349             this.root.select();
16350             if(callback){
16351                 callback(true, this.root);
16352             }
16353         }
16354     },
16355
16356     getTreeEl : function(){
16357         return this.el;
16358     },
16359
16360     /**
16361      * Trigger rendering of this TreePanel
16362      */
16363     render : function(){
16364         if (this.innerCt) {
16365             return this; // stop it rendering more than once!!
16366         }
16367         
16368         this.innerCt = this.el.createChild({tag:"ul",
16369                cls:"x-tree-root-ct " +
16370                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16371
16372         if(this.containerScroll){
16373             Roo.dd.ScrollManager.register(this.el);
16374         }
16375         if((this.enableDD || this.enableDrop) && !this.dropZone){
16376            /**
16377             * The dropZone used by this tree if drop is enabled
16378             * @type Roo.tree.TreeDropZone
16379             */
16380              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16381                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16382            });
16383         }
16384         if((this.enableDD || this.enableDrag) && !this.dragZone){
16385            /**
16386             * The dragZone used by this tree if drag is enabled
16387             * @type Roo.tree.TreeDragZone
16388             */
16389             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16390                ddGroup: this.ddGroup || "TreeDD",
16391                scroll: this.ddScroll
16392            });
16393         }
16394         this.getSelectionModel().init(this);
16395         if (!this.root) {
16396             Roo.log("ROOT not set in tree");
16397             return this;
16398         }
16399         this.root.render();
16400         if(!this.rootVisible){
16401             this.root.renderChildren();
16402         }
16403         return this;
16404     }
16405 });/*
16406  * Based on:
16407  * Ext JS Library 1.1.1
16408  * Copyright(c) 2006-2007, Ext JS, LLC.
16409  *
16410  * Originally Released Under LGPL - original licence link has changed is not relivant.
16411  *
16412  * Fork - LGPL
16413  * <script type="text/javascript">
16414  */
16415  
16416
16417 /**
16418  * @class Roo.tree.DefaultSelectionModel
16419  * @extends Roo.util.Observable
16420  * The default single selection for a TreePanel.
16421  * @param {Object} cfg Configuration
16422  */
16423 Roo.tree.DefaultSelectionModel = function(cfg){
16424    this.selNode = null;
16425    
16426    
16427    
16428    this.addEvents({
16429        /**
16430         * @event selectionchange
16431         * Fires when the selected node changes
16432         * @param {DefaultSelectionModel} this
16433         * @param {TreeNode} node the new selection
16434         */
16435        "selectionchange" : true,
16436
16437        /**
16438         * @event beforeselect
16439         * Fires before the selected node changes, return false to cancel the change
16440         * @param {DefaultSelectionModel} this
16441         * @param {TreeNode} node the new selection
16442         * @param {TreeNode} node the old selection
16443         */
16444        "beforeselect" : true
16445    });
16446    
16447     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16448 };
16449
16450 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16451     init : function(tree){
16452         this.tree = tree;
16453         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16454         tree.on("click", this.onNodeClick, this);
16455     },
16456     
16457     onNodeClick : function(node, e){
16458         if (e.ctrlKey && this.selNode == node)  {
16459             this.unselect(node);
16460             return;
16461         }
16462         this.select(node);
16463     },
16464     
16465     /**
16466      * Select a node.
16467      * @param {TreeNode} node The node to select
16468      * @return {TreeNode} The selected node
16469      */
16470     select : function(node){
16471         var last = this.selNode;
16472         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16473             if(last){
16474                 last.ui.onSelectedChange(false);
16475             }
16476             this.selNode = node;
16477             node.ui.onSelectedChange(true);
16478             this.fireEvent("selectionchange", this, node, last);
16479         }
16480         return node;
16481     },
16482     
16483     /**
16484      * Deselect a node.
16485      * @param {TreeNode} node The node to unselect
16486      */
16487     unselect : function(node){
16488         if(this.selNode == node){
16489             this.clearSelections();
16490         }    
16491     },
16492     
16493     /**
16494      * Clear all selections
16495      */
16496     clearSelections : function(){
16497         var n = this.selNode;
16498         if(n){
16499             n.ui.onSelectedChange(false);
16500             this.selNode = null;
16501             this.fireEvent("selectionchange", this, null);
16502         }
16503         return n;
16504     },
16505     
16506     /**
16507      * Get the selected node
16508      * @return {TreeNode} The selected node
16509      */
16510     getSelectedNode : function(){
16511         return this.selNode;    
16512     },
16513     
16514     /**
16515      * Returns true if the node is selected
16516      * @param {TreeNode} node The node to check
16517      * @return {Boolean}
16518      */
16519     isSelected : function(node){
16520         return this.selNode == node;  
16521     },
16522
16523     /**
16524      * Selects the node above the selected node in the tree, intelligently walking the nodes
16525      * @return TreeNode The new selection
16526      */
16527     selectPrevious : function(){
16528         var s = this.selNode || this.lastSelNode;
16529         if(!s){
16530             return null;
16531         }
16532         var ps = s.previousSibling;
16533         if(ps){
16534             if(!ps.isExpanded() || ps.childNodes.length < 1){
16535                 return this.select(ps);
16536             } else{
16537                 var lc = ps.lastChild;
16538                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16539                     lc = lc.lastChild;
16540                 }
16541                 return this.select(lc);
16542             }
16543         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16544             return this.select(s.parentNode);
16545         }
16546         return null;
16547     },
16548
16549     /**
16550      * Selects the node above the selected node in the tree, intelligently walking the nodes
16551      * @return TreeNode The new selection
16552      */
16553     selectNext : function(){
16554         var s = this.selNode || this.lastSelNode;
16555         if(!s){
16556             return null;
16557         }
16558         if(s.firstChild && s.isExpanded()){
16559              return this.select(s.firstChild);
16560          }else if(s.nextSibling){
16561              return this.select(s.nextSibling);
16562          }else if(s.parentNode){
16563             var newS = null;
16564             s.parentNode.bubble(function(){
16565                 if(this.nextSibling){
16566                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16567                     return false;
16568                 }
16569             });
16570             return newS;
16571          }
16572         return null;
16573     },
16574
16575     onKeyDown : function(e){
16576         var s = this.selNode || this.lastSelNode;
16577         // undesirable, but required
16578         var sm = this;
16579         if(!s){
16580             return;
16581         }
16582         var k = e.getKey();
16583         switch(k){
16584              case e.DOWN:
16585                  e.stopEvent();
16586                  this.selectNext();
16587              break;
16588              case e.UP:
16589                  e.stopEvent();
16590                  this.selectPrevious();
16591              break;
16592              case e.RIGHT:
16593                  e.preventDefault();
16594                  if(s.hasChildNodes()){
16595                      if(!s.isExpanded()){
16596                          s.expand();
16597                      }else if(s.firstChild){
16598                          this.select(s.firstChild, e);
16599                      }
16600                  }
16601              break;
16602              case e.LEFT:
16603                  e.preventDefault();
16604                  if(s.hasChildNodes() && s.isExpanded()){
16605                      s.collapse();
16606                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16607                      this.select(s.parentNode, e);
16608                  }
16609              break;
16610         };
16611     }
16612 });
16613
16614 /**
16615  * @class Roo.tree.MultiSelectionModel
16616  * @extends Roo.util.Observable
16617  * Multi selection for a TreePanel.
16618  * @param {Object} cfg Configuration
16619  */
16620 Roo.tree.MultiSelectionModel = function(){
16621    this.selNodes = [];
16622    this.selMap = {};
16623    this.addEvents({
16624        /**
16625         * @event selectionchange
16626         * Fires when the selected nodes change
16627         * @param {MultiSelectionModel} this
16628         * @param {Array} nodes Array of the selected nodes
16629         */
16630        "selectionchange" : true
16631    });
16632    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16633    
16634 };
16635
16636 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16637     init : function(tree){
16638         this.tree = tree;
16639         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16640         tree.on("click", this.onNodeClick, this);
16641     },
16642     
16643     onNodeClick : function(node, e){
16644         this.select(node, e, e.ctrlKey);
16645     },
16646     
16647     /**
16648      * Select a node.
16649      * @param {TreeNode} node The node to select
16650      * @param {EventObject} e (optional) An event associated with the selection
16651      * @param {Boolean} keepExisting True to retain existing selections
16652      * @return {TreeNode} The selected node
16653      */
16654     select : function(node, e, keepExisting){
16655         if(keepExisting !== true){
16656             this.clearSelections(true);
16657         }
16658         if(this.isSelected(node)){
16659             this.lastSelNode = node;
16660             return node;
16661         }
16662         this.selNodes.push(node);
16663         this.selMap[node.id] = node;
16664         this.lastSelNode = node;
16665         node.ui.onSelectedChange(true);
16666         this.fireEvent("selectionchange", this, this.selNodes);
16667         return node;
16668     },
16669     
16670     /**
16671      * Deselect a node.
16672      * @param {TreeNode} node The node to unselect
16673      */
16674     unselect : function(node){
16675         if(this.selMap[node.id]){
16676             node.ui.onSelectedChange(false);
16677             var sn = this.selNodes;
16678             var index = -1;
16679             if(sn.indexOf){
16680                 index = sn.indexOf(node);
16681             }else{
16682                 for(var i = 0, len = sn.length; i < len; i++){
16683                     if(sn[i] == node){
16684                         index = i;
16685                         break;
16686                     }
16687                 }
16688             }
16689             if(index != -1){
16690                 this.selNodes.splice(index, 1);
16691             }
16692             delete this.selMap[node.id];
16693             this.fireEvent("selectionchange", this, this.selNodes);
16694         }
16695     },
16696     
16697     /**
16698      * Clear all selections
16699      */
16700     clearSelections : function(suppressEvent){
16701         var sn = this.selNodes;
16702         if(sn.length > 0){
16703             for(var i = 0, len = sn.length; i < len; i++){
16704                 sn[i].ui.onSelectedChange(false);
16705             }
16706             this.selNodes = [];
16707             this.selMap = {};
16708             if(suppressEvent !== true){
16709                 this.fireEvent("selectionchange", this, this.selNodes);
16710             }
16711         }
16712     },
16713     
16714     /**
16715      * Returns true if the node is selected
16716      * @param {TreeNode} node The node to check
16717      * @return {Boolean}
16718      */
16719     isSelected : function(node){
16720         return this.selMap[node.id] ? true : false;  
16721     },
16722     
16723     /**
16724      * Returns an array of the selected nodes
16725      * @return {Array}
16726      */
16727     getSelectedNodes : function(){
16728         return this.selNodes;    
16729     },
16730
16731     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16732
16733     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16734
16735     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16736 });/*
16737  * Based on:
16738  * Ext JS Library 1.1.1
16739  * Copyright(c) 2006-2007, Ext JS, LLC.
16740  *
16741  * Originally Released Under LGPL - original licence link has changed is not relivant.
16742  *
16743  * Fork - LGPL
16744  * <script type="text/javascript">
16745  */
16746  
16747 /**
16748  * @class Roo.tree.TreeNode
16749  * @extends Roo.data.Node
16750  * @cfg {String} text The text for this node
16751  * @cfg {Boolean} expanded true to start the node expanded
16752  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16753  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16754  * @cfg {Boolean} disabled true to start the node disabled
16755  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16756  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16757  * @cfg {String} cls A css class to be added to the node
16758  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16759  * @cfg {String} href URL of the link used for the node (defaults to #)
16760  * @cfg {String} hrefTarget target frame for the link
16761  * @cfg {String} qtip An Ext QuickTip for the node
16762  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16763  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16764  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16765  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16766  * (defaults to undefined with no checkbox rendered)
16767  * @constructor
16768  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16769  */
16770 Roo.tree.TreeNode = function(attributes){
16771     attributes = attributes || {};
16772     if(typeof attributes == "string"){
16773         attributes = {text: attributes};
16774     }
16775     this.childrenRendered = false;
16776     this.rendered = false;
16777     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16778     this.expanded = attributes.expanded === true;
16779     this.isTarget = attributes.isTarget !== false;
16780     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16781     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16782
16783     /**
16784      * Read-only. The text for this node. To change it use setText().
16785      * @type String
16786      */
16787     this.text = attributes.text;
16788     /**
16789      * True if this node is disabled.
16790      * @type Boolean
16791      */
16792     this.disabled = attributes.disabled === true;
16793
16794     this.addEvents({
16795         /**
16796         * @event textchange
16797         * Fires when the text for this node is changed
16798         * @param {Node} this This node
16799         * @param {String} text The new text
16800         * @param {String} oldText The old text
16801         */
16802         "textchange" : true,
16803         /**
16804         * @event beforeexpand
16805         * Fires before this node is expanded, return false to cancel.
16806         * @param {Node} this This node
16807         * @param {Boolean} deep
16808         * @param {Boolean} anim
16809         */
16810         "beforeexpand" : true,
16811         /**
16812         * @event beforecollapse
16813         * Fires before this node is collapsed, return false to cancel.
16814         * @param {Node} this This node
16815         * @param {Boolean} deep
16816         * @param {Boolean} anim
16817         */
16818         "beforecollapse" : true,
16819         /**
16820         * @event expand
16821         * Fires when this node is expanded
16822         * @param {Node} this This node
16823         */
16824         "expand" : true,
16825         /**
16826         * @event disabledchange
16827         * Fires when the disabled status of this node changes
16828         * @param {Node} this This node
16829         * @param {Boolean} disabled
16830         */
16831         "disabledchange" : true,
16832         /**
16833         * @event collapse
16834         * Fires when this node is collapsed
16835         * @param {Node} this This node
16836         */
16837         "collapse" : true,
16838         /**
16839         * @event beforeclick
16840         * Fires before click processing. Return false to cancel the default action.
16841         * @param {Node} this This node
16842         * @param {Roo.EventObject} e The event object
16843         */
16844         "beforeclick":true,
16845         /**
16846         * @event checkchange
16847         * Fires when a node with a checkbox's checked property changes
16848         * @param {Node} this This node
16849         * @param {Boolean} checked
16850         */
16851         "checkchange":true,
16852         /**
16853         * @event click
16854         * Fires when this node is clicked
16855         * @param {Node} this This node
16856         * @param {Roo.EventObject} e The event object
16857         */
16858         "click":true,
16859         /**
16860         * @event dblclick
16861         * Fires when this node is double clicked
16862         * @param {Node} this This node
16863         * @param {Roo.EventObject} e The event object
16864         */
16865         "dblclick":true,
16866         /**
16867         * @event contextmenu
16868         * Fires when this node is right clicked
16869         * @param {Node} this This node
16870         * @param {Roo.EventObject} e The event object
16871         */
16872         "contextmenu":true,
16873         /**
16874         * @event beforechildrenrendered
16875         * Fires right before the child nodes for this node are rendered
16876         * @param {Node} this This node
16877         */
16878         "beforechildrenrendered":true
16879     });
16880
16881     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16882
16883     /**
16884      * Read-only. The UI for this node
16885      * @type TreeNodeUI
16886      */
16887     this.ui = new uiClass(this);
16888     
16889     // finally support items[]
16890     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16891         return;
16892     }
16893     
16894     
16895     Roo.each(this.attributes.items, function(c) {
16896         this.appendChild(Roo.factory(c,Roo.Tree));
16897     }, this);
16898     delete this.attributes.items;
16899     
16900     
16901     
16902 };
16903 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16904     preventHScroll: true,
16905     /**
16906      * Returns true if this node is expanded
16907      * @return {Boolean}
16908      */
16909     isExpanded : function(){
16910         return this.expanded;
16911     },
16912
16913     /**
16914      * Returns the UI object for this node
16915      * @return {TreeNodeUI}
16916      */
16917     getUI : function(){
16918         return this.ui;
16919     },
16920
16921     // private override
16922     setFirstChild : function(node){
16923         var of = this.firstChild;
16924         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16925         if(this.childrenRendered && of && node != of){
16926             of.renderIndent(true, true);
16927         }
16928         if(this.rendered){
16929             this.renderIndent(true, true);
16930         }
16931     },
16932
16933     // private override
16934     setLastChild : function(node){
16935         var ol = this.lastChild;
16936         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16937         if(this.childrenRendered && ol && node != ol){
16938             ol.renderIndent(true, true);
16939         }
16940         if(this.rendered){
16941             this.renderIndent(true, true);
16942         }
16943     },
16944
16945     // these methods are overridden to provide lazy rendering support
16946     // private override
16947     appendChild : function()
16948     {
16949         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16950         if(node && this.childrenRendered){
16951             node.render();
16952         }
16953         this.ui.updateExpandIcon();
16954         return node;
16955     },
16956
16957     // private override
16958     removeChild : function(node){
16959         this.ownerTree.getSelectionModel().unselect(node);
16960         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16961         // if it's been rendered remove dom node
16962         if(this.childrenRendered){
16963             node.ui.remove();
16964         }
16965         if(this.childNodes.length < 1){
16966             this.collapse(false, false);
16967         }else{
16968             this.ui.updateExpandIcon();
16969         }
16970         if(!this.firstChild) {
16971             this.childrenRendered = false;
16972         }
16973         return node;
16974     },
16975
16976     // private override
16977     insertBefore : function(node, refNode){
16978         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16979         if(newNode && refNode && this.childrenRendered){
16980             node.render();
16981         }
16982         this.ui.updateExpandIcon();
16983         return newNode;
16984     },
16985
16986     /**
16987      * Sets the text for this node
16988      * @param {String} text
16989      */
16990     setText : function(text){
16991         var oldText = this.text;
16992         this.text = text;
16993         this.attributes.text = text;
16994         if(this.rendered){ // event without subscribing
16995             this.ui.onTextChange(this, text, oldText);
16996         }
16997         this.fireEvent("textchange", this, text, oldText);
16998     },
16999
17000     /**
17001      * Triggers selection of this node
17002      */
17003     select : function(){
17004         this.getOwnerTree().getSelectionModel().select(this);
17005     },
17006
17007     /**
17008      * Triggers deselection of this node
17009      */
17010     unselect : function(){
17011         this.getOwnerTree().getSelectionModel().unselect(this);
17012     },
17013
17014     /**
17015      * Returns true if this node is selected
17016      * @return {Boolean}
17017      */
17018     isSelected : function(){
17019         return this.getOwnerTree().getSelectionModel().isSelected(this);
17020     },
17021
17022     /**
17023      * Expand this node.
17024      * @param {Boolean} deep (optional) True to expand all children as well
17025      * @param {Boolean} anim (optional) false to cancel the default animation
17026      * @param {Function} callback (optional) A callback to be called when
17027      * expanding this node completes (does not wait for deep expand to complete).
17028      * Called with 1 parameter, this node.
17029      */
17030     expand : function(deep, anim, callback){
17031         if(!this.expanded){
17032             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17033                 return;
17034             }
17035             if(!this.childrenRendered){
17036                 this.renderChildren();
17037             }
17038             this.expanded = true;
17039             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17040                 this.ui.animExpand(function(){
17041                     this.fireEvent("expand", this);
17042                     if(typeof callback == "function"){
17043                         callback(this);
17044                     }
17045                     if(deep === true){
17046                         this.expandChildNodes(true);
17047                     }
17048                 }.createDelegate(this));
17049                 return;
17050             }else{
17051                 this.ui.expand();
17052                 this.fireEvent("expand", this);
17053                 if(typeof callback == "function"){
17054                     callback(this);
17055                 }
17056             }
17057         }else{
17058            if(typeof callback == "function"){
17059                callback(this);
17060            }
17061         }
17062         if(deep === true){
17063             this.expandChildNodes(true);
17064         }
17065     },
17066
17067     isHiddenRoot : function(){
17068         return this.isRoot && !this.getOwnerTree().rootVisible;
17069     },
17070
17071     /**
17072      * Collapse this node.
17073      * @param {Boolean} deep (optional) True to collapse all children as well
17074      * @param {Boolean} anim (optional) false to cancel the default animation
17075      */
17076     collapse : function(deep, anim){
17077         if(this.expanded && !this.isHiddenRoot()){
17078             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17079                 return;
17080             }
17081             this.expanded = false;
17082             if((this.getOwnerTree().animate && anim !== false) || anim){
17083                 this.ui.animCollapse(function(){
17084                     this.fireEvent("collapse", this);
17085                     if(deep === true){
17086                         this.collapseChildNodes(true);
17087                     }
17088                 }.createDelegate(this));
17089                 return;
17090             }else{
17091                 this.ui.collapse();
17092                 this.fireEvent("collapse", this);
17093             }
17094         }
17095         if(deep === true){
17096             var cs = this.childNodes;
17097             for(var i = 0, len = cs.length; i < len; i++) {
17098                 cs[i].collapse(true, false);
17099             }
17100         }
17101     },
17102
17103     // private
17104     delayedExpand : function(delay){
17105         if(!this.expandProcId){
17106             this.expandProcId = this.expand.defer(delay, this);
17107         }
17108     },
17109
17110     // private
17111     cancelExpand : function(){
17112         if(this.expandProcId){
17113             clearTimeout(this.expandProcId);
17114         }
17115         this.expandProcId = false;
17116     },
17117
17118     /**
17119      * Toggles expanded/collapsed state of the node
17120      */
17121     toggle : function(){
17122         if(this.expanded){
17123             this.collapse();
17124         }else{
17125             this.expand();
17126         }
17127     },
17128
17129     /**
17130      * Ensures all parent nodes are expanded
17131      */
17132     ensureVisible : function(callback){
17133         var tree = this.getOwnerTree();
17134         tree.expandPath(this.parentNode.getPath(), false, function(){
17135             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17136             Roo.callback(callback);
17137         }.createDelegate(this));
17138     },
17139
17140     /**
17141      * Expand all child nodes
17142      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17143      */
17144     expandChildNodes : function(deep){
17145         var cs = this.childNodes;
17146         for(var i = 0, len = cs.length; i < len; i++) {
17147                 cs[i].expand(deep);
17148         }
17149     },
17150
17151     /**
17152      * Collapse all child nodes
17153      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17154      */
17155     collapseChildNodes : function(deep){
17156         var cs = this.childNodes;
17157         for(var i = 0, len = cs.length; i < len; i++) {
17158                 cs[i].collapse(deep);
17159         }
17160     },
17161
17162     /**
17163      * Disables this node
17164      */
17165     disable : function(){
17166         this.disabled = true;
17167         this.unselect();
17168         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17169             this.ui.onDisableChange(this, true);
17170         }
17171         this.fireEvent("disabledchange", this, true);
17172     },
17173
17174     /**
17175      * Enables this node
17176      */
17177     enable : function(){
17178         this.disabled = false;
17179         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17180             this.ui.onDisableChange(this, false);
17181         }
17182         this.fireEvent("disabledchange", this, false);
17183     },
17184
17185     // private
17186     renderChildren : function(suppressEvent){
17187         if(suppressEvent !== false){
17188             this.fireEvent("beforechildrenrendered", this);
17189         }
17190         var cs = this.childNodes;
17191         for(var i = 0, len = cs.length; i < len; i++){
17192             cs[i].render(true);
17193         }
17194         this.childrenRendered = true;
17195     },
17196
17197     // private
17198     sort : function(fn, scope){
17199         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17200         if(this.childrenRendered){
17201             var cs = this.childNodes;
17202             for(var i = 0, len = cs.length; i < len; i++){
17203                 cs[i].render(true);
17204             }
17205         }
17206     },
17207
17208     // private
17209     render : function(bulkRender){
17210         this.ui.render(bulkRender);
17211         if(!this.rendered){
17212             this.rendered = true;
17213             if(this.expanded){
17214                 this.expanded = false;
17215                 this.expand(false, false);
17216             }
17217         }
17218     },
17219
17220     // private
17221     renderIndent : function(deep, refresh){
17222         if(refresh){
17223             this.ui.childIndent = null;
17224         }
17225         this.ui.renderIndent();
17226         if(deep === true && this.childrenRendered){
17227             var cs = this.childNodes;
17228             for(var i = 0, len = cs.length; i < len; i++){
17229                 cs[i].renderIndent(true, refresh);
17230             }
17231         }
17232     }
17233 });/*
17234  * Based on:
17235  * Ext JS Library 1.1.1
17236  * Copyright(c) 2006-2007, Ext JS, LLC.
17237  *
17238  * Originally Released Under LGPL - original licence link has changed is not relivant.
17239  *
17240  * Fork - LGPL
17241  * <script type="text/javascript">
17242  */
17243  
17244 /**
17245  * @class Roo.tree.AsyncTreeNode
17246  * @extends Roo.tree.TreeNode
17247  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17248  * @constructor
17249  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17250  */
17251  Roo.tree.AsyncTreeNode = function(config){
17252     this.loaded = false;
17253     this.loading = false;
17254     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17255     /**
17256     * @event beforeload
17257     * Fires before this node is loaded, return false to cancel
17258     * @param {Node} this This node
17259     */
17260     this.addEvents({'beforeload':true, 'load': true});
17261     /**
17262     * @event load
17263     * Fires when this node is loaded
17264     * @param {Node} this This node
17265     */
17266     /**
17267      * The loader used by this node (defaults to using the tree's defined loader)
17268      * @type TreeLoader
17269      * @property loader
17270      */
17271 };
17272 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17273     expand : function(deep, anim, callback){
17274         if(this.loading){ // if an async load is already running, waiting til it's done
17275             var timer;
17276             var f = function(){
17277                 if(!this.loading){ // done loading
17278                     clearInterval(timer);
17279                     this.expand(deep, anim, callback);
17280                 }
17281             }.createDelegate(this);
17282             timer = setInterval(f, 200);
17283             return;
17284         }
17285         if(!this.loaded){
17286             if(this.fireEvent("beforeload", this) === false){
17287                 return;
17288             }
17289             this.loading = true;
17290             this.ui.beforeLoad(this);
17291             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17292             if(loader){
17293                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17294                 return;
17295             }
17296         }
17297         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17298     },
17299     
17300     /**
17301      * Returns true if this node is currently loading
17302      * @return {Boolean}
17303      */
17304     isLoading : function(){
17305         return this.loading;  
17306     },
17307     
17308     loadComplete : function(deep, anim, callback){
17309         this.loading = false;
17310         this.loaded = true;
17311         this.ui.afterLoad(this);
17312         this.fireEvent("load", this);
17313         this.expand(deep, anim, callback);
17314     },
17315     
17316     /**
17317      * Returns true if this node has been loaded
17318      * @return {Boolean}
17319      */
17320     isLoaded : function(){
17321         return this.loaded;
17322     },
17323     
17324     hasChildNodes : function(){
17325         if(!this.isLeaf() && !this.loaded){
17326             return true;
17327         }else{
17328             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17329         }
17330     },
17331
17332     /**
17333      * Trigger a reload for this node
17334      * @param {Function} callback
17335      */
17336     reload : function(callback){
17337         this.collapse(false, false);
17338         while(this.firstChild){
17339             this.removeChild(this.firstChild);
17340         }
17341         this.childrenRendered = false;
17342         this.loaded = false;
17343         if(this.isHiddenRoot()){
17344             this.expanded = false;
17345         }
17346         this.expand(false, false, callback);
17347     }
17348 });/*
17349  * Based on:
17350  * Ext JS Library 1.1.1
17351  * Copyright(c) 2006-2007, Ext JS, LLC.
17352  *
17353  * Originally Released Under LGPL - original licence link has changed is not relivant.
17354  *
17355  * Fork - LGPL
17356  * <script type="text/javascript">
17357  */
17358  
17359 /**
17360  * @class Roo.tree.TreeNodeUI
17361  * @constructor
17362  * @param {Object} node The node to render
17363  * The TreeNode UI implementation is separate from the
17364  * tree implementation. Unless you are customizing the tree UI,
17365  * you should never have to use this directly.
17366  */
17367 Roo.tree.TreeNodeUI = function(node){
17368     this.node = node;
17369     this.rendered = false;
17370     this.animating = false;
17371     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17372 };
17373
17374 Roo.tree.TreeNodeUI.prototype = {
17375     removeChild : function(node){
17376         if(this.rendered){
17377             this.ctNode.removeChild(node.ui.getEl());
17378         }
17379     },
17380
17381     beforeLoad : function(){
17382          this.addClass("x-tree-node-loading");
17383     },
17384
17385     afterLoad : function(){
17386          this.removeClass("x-tree-node-loading");
17387     },
17388
17389     onTextChange : function(node, text, oldText){
17390         if(this.rendered){
17391             this.textNode.innerHTML = text;
17392         }
17393     },
17394
17395     onDisableChange : function(node, state){
17396         this.disabled = state;
17397         if(state){
17398             this.addClass("x-tree-node-disabled");
17399         }else{
17400             this.removeClass("x-tree-node-disabled");
17401         }
17402     },
17403
17404     onSelectedChange : function(state){
17405         if(state){
17406             this.focus();
17407             this.addClass("x-tree-selected");
17408         }else{
17409             //this.blur();
17410             this.removeClass("x-tree-selected");
17411         }
17412     },
17413
17414     onMove : function(tree, node, oldParent, newParent, index, refNode){
17415         this.childIndent = null;
17416         if(this.rendered){
17417             var targetNode = newParent.ui.getContainer();
17418             if(!targetNode){//target not rendered
17419                 this.holder = document.createElement("div");
17420                 this.holder.appendChild(this.wrap);
17421                 return;
17422             }
17423             var insertBefore = refNode ? refNode.ui.getEl() : null;
17424             if(insertBefore){
17425                 targetNode.insertBefore(this.wrap, insertBefore);
17426             }else{
17427                 targetNode.appendChild(this.wrap);
17428             }
17429             this.node.renderIndent(true);
17430         }
17431     },
17432
17433     addClass : function(cls){
17434         if(this.elNode){
17435             Roo.fly(this.elNode).addClass(cls);
17436         }
17437     },
17438
17439     removeClass : function(cls){
17440         if(this.elNode){
17441             Roo.fly(this.elNode).removeClass(cls);
17442         }
17443     },
17444
17445     remove : function(){
17446         if(this.rendered){
17447             this.holder = document.createElement("div");
17448             this.holder.appendChild(this.wrap);
17449         }
17450     },
17451
17452     fireEvent : function(){
17453         return this.node.fireEvent.apply(this.node, arguments);
17454     },
17455
17456     initEvents : function(){
17457         this.node.on("move", this.onMove, this);
17458         var E = Roo.EventManager;
17459         var a = this.anchor;
17460
17461         var el = Roo.fly(a, '_treeui');
17462
17463         if(Roo.isOpera){ // opera render bug ignores the CSS
17464             el.setStyle("text-decoration", "none");
17465         }
17466
17467         el.on("click", this.onClick, this);
17468         el.on("dblclick", this.onDblClick, this);
17469
17470         if(this.checkbox){
17471             Roo.EventManager.on(this.checkbox,
17472                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17473         }
17474
17475         el.on("contextmenu", this.onContextMenu, this);
17476
17477         var icon = Roo.fly(this.iconNode);
17478         icon.on("click", this.onClick, this);
17479         icon.on("dblclick", this.onDblClick, this);
17480         icon.on("contextmenu", this.onContextMenu, this);
17481         E.on(this.ecNode, "click", this.ecClick, this, true);
17482
17483         if(this.node.disabled){
17484             this.addClass("x-tree-node-disabled");
17485         }
17486         if(this.node.hidden){
17487             this.addClass("x-tree-node-disabled");
17488         }
17489         var ot = this.node.getOwnerTree();
17490         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17491         if(dd && (!this.node.isRoot || ot.rootVisible)){
17492             Roo.dd.Registry.register(this.elNode, {
17493                 node: this.node,
17494                 handles: this.getDDHandles(),
17495                 isHandle: false
17496             });
17497         }
17498     },
17499
17500     getDDHandles : function(){
17501         return [this.iconNode, this.textNode];
17502     },
17503
17504     hide : function(){
17505         if(this.rendered){
17506             this.wrap.style.display = "none";
17507         }
17508     },
17509
17510     show : function(){
17511         if(this.rendered){
17512             this.wrap.style.display = "";
17513         }
17514     },
17515
17516     onContextMenu : function(e){
17517         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17518             e.preventDefault();
17519             this.focus();
17520             this.fireEvent("contextmenu", this.node, e);
17521         }
17522     },
17523
17524     onClick : function(e){
17525         if(this.dropping){
17526             e.stopEvent();
17527             return;
17528         }
17529         if(this.fireEvent("beforeclick", this.node, e) !== false){
17530             if(!this.disabled && this.node.attributes.href){
17531                 this.fireEvent("click", this.node, e);
17532                 return;
17533             }
17534             e.preventDefault();
17535             if(this.disabled){
17536                 return;
17537             }
17538
17539             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17540                 this.node.toggle();
17541             }
17542
17543             this.fireEvent("click", this.node, e);
17544         }else{
17545             e.stopEvent();
17546         }
17547     },
17548
17549     onDblClick : function(e){
17550         e.preventDefault();
17551         if(this.disabled){
17552             return;
17553         }
17554         if(this.checkbox){
17555             this.toggleCheck();
17556         }
17557         if(!this.animating && this.node.hasChildNodes()){
17558             this.node.toggle();
17559         }
17560         this.fireEvent("dblclick", this.node, e);
17561     },
17562
17563     onCheckChange : function(){
17564         var checked = this.checkbox.checked;
17565         this.node.attributes.checked = checked;
17566         this.fireEvent('checkchange', this.node, checked);
17567     },
17568
17569     ecClick : function(e){
17570         if(!this.animating && this.node.hasChildNodes()){
17571             this.node.toggle();
17572         }
17573     },
17574
17575     startDrop : function(){
17576         this.dropping = true;
17577     },
17578
17579     // delayed drop so the click event doesn't get fired on a drop
17580     endDrop : function(){
17581        setTimeout(function(){
17582            this.dropping = false;
17583        }.createDelegate(this), 50);
17584     },
17585
17586     expand : function(){
17587         this.updateExpandIcon();
17588         this.ctNode.style.display = "";
17589     },
17590
17591     focus : function(){
17592         if(!this.node.preventHScroll){
17593             try{this.anchor.focus();
17594             }catch(e){}
17595         }else if(!Roo.isIE){
17596             try{
17597                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17598                 var l = noscroll.scrollLeft;
17599                 this.anchor.focus();
17600                 noscroll.scrollLeft = l;
17601             }catch(e){}
17602         }
17603     },
17604
17605     toggleCheck : function(value){
17606         var cb = this.checkbox;
17607         if(cb){
17608             cb.checked = (value === undefined ? !cb.checked : value);
17609         }
17610     },
17611
17612     blur : function(){
17613         try{
17614             this.anchor.blur();
17615         }catch(e){}
17616     },
17617
17618     animExpand : function(callback){
17619         var ct = Roo.get(this.ctNode);
17620         ct.stopFx();
17621         if(!this.node.hasChildNodes()){
17622             this.updateExpandIcon();
17623             this.ctNode.style.display = "";
17624             Roo.callback(callback);
17625             return;
17626         }
17627         this.animating = true;
17628         this.updateExpandIcon();
17629
17630         ct.slideIn('t', {
17631            callback : function(){
17632                this.animating = false;
17633                Roo.callback(callback);
17634             },
17635             scope: this,
17636             duration: this.node.ownerTree.duration || .25
17637         });
17638     },
17639
17640     highlight : function(){
17641         var tree = this.node.getOwnerTree();
17642         Roo.fly(this.wrap).highlight(
17643             tree.hlColor || "C3DAF9",
17644             {endColor: tree.hlBaseColor}
17645         );
17646     },
17647
17648     collapse : function(){
17649         this.updateExpandIcon();
17650         this.ctNode.style.display = "none";
17651     },
17652
17653     animCollapse : function(callback){
17654         var ct = Roo.get(this.ctNode);
17655         ct.enableDisplayMode('block');
17656         ct.stopFx();
17657
17658         this.animating = true;
17659         this.updateExpandIcon();
17660
17661         ct.slideOut('t', {
17662             callback : function(){
17663                this.animating = false;
17664                Roo.callback(callback);
17665             },
17666             scope: this,
17667             duration: this.node.ownerTree.duration || .25
17668         });
17669     },
17670
17671     getContainer : function(){
17672         return this.ctNode;
17673     },
17674
17675     getEl : function(){
17676         return this.wrap;
17677     },
17678
17679     appendDDGhost : function(ghostNode){
17680         ghostNode.appendChild(this.elNode.cloneNode(true));
17681     },
17682
17683     getDDRepairXY : function(){
17684         return Roo.lib.Dom.getXY(this.iconNode);
17685     },
17686
17687     onRender : function(){
17688         this.render();
17689     },
17690
17691     render : function(bulkRender){
17692         var n = this.node, a = n.attributes;
17693         var targetNode = n.parentNode ?
17694               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17695
17696         if(!this.rendered){
17697             this.rendered = true;
17698
17699             this.renderElements(n, a, targetNode, bulkRender);
17700
17701             if(a.qtip){
17702                if(this.textNode.setAttributeNS){
17703                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17704                    if(a.qtipTitle){
17705                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17706                    }
17707                }else{
17708                    this.textNode.setAttribute("ext:qtip", a.qtip);
17709                    if(a.qtipTitle){
17710                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17711                    }
17712                }
17713             }else if(a.qtipCfg){
17714                 a.qtipCfg.target = Roo.id(this.textNode);
17715                 Roo.QuickTips.register(a.qtipCfg);
17716             }
17717             this.initEvents();
17718             if(!this.node.expanded){
17719                 this.updateExpandIcon();
17720             }
17721         }else{
17722             if(bulkRender === true) {
17723                 targetNode.appendChild(this.wrap);
17724             }
17725         }
17726     },
17727
17728     renderElements : function(n, a, targetNode, bulkRender)
17729     {
17730         // add some indent caching, this helps performance when rendering a large tree
17731         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17732         var t = n.getOwnerTree();
17733         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17734         if (typeof(n.attributes.html) != 'undefined') {
17735             txt = n.attributes.html;
17736         }
17737         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17738         var cb = typeof a.checked == 'boolean';
17739         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17740         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17741             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17742             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17743             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17744             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17745             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17746              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17747                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17748             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17749             "</li>"];
17750
17751         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17752             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17753                                 n.nextSibling.ui.getEl(), buf.join(""));
17754         }else{
17755             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17756         }
17757
17758         this.elNode = this.wrap.childNodes[0];
17759         this.ctNode = this.wrap.childNodes[1];
17760         var cs = this.elNode.childNodes;
17761         this.indentNode = cs[0];
17762         this.ecNode = cs[1];
17763         this.iconNode = cs[2];
17764         var index = 3;
17765         if(cb){
17766             this.checkbox = cs[3];
17767             index++;
17768         }
17769         this.anchor = cs[index];
17770         this.textNode = cs[index].firstChild;
17771     },
17772
17773     getAnchor : function(){
17774         return this.anchor;
17775     },
17776
17777     getTextEl : function(){
17778         return this.textNode;
17779     },
17780
17781     getIconEl : function(){
17782         return this.iconNode;
17783     },
17784
17785     isChecked : function(){
17786         return this.checkbox ? this.checkbox.checked : false;
17787     },
17788
17789     updateExpandIcon : function(){
17790         if(this.rendered){
17791             var n = this.node, c1, c2;
17792             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17793             var hasChild = n.hasChildNodes();
17794             if(hasChild){
17795                 if(n.expanded){
17796                     cls += "-minus";
17797                     c1 = "x-tree-node-collapsed";
17798                     c2 = "x-tree-node-expanded";
17799                 }else{
17800                     cls += "-plus";
17801                     c1 = "x-tree-node-expanded";
17802                     c2 = "x-tree-node-collapsed";
17803                 }
17804                 if(this.wasLeaf){
17805                     this.removeClass("x-tree-node-leaf");
17806                     this.wasLeaf = false;
17807                 }
17808                 if(this.c1 != c1 || this.c2 != c2){
17809                     Roo.fly(this.elNode).replaceClass(c1, c2);
17810                     this.c1 = c1; this.c2 = c2;
17811                 }
17812             }else{
17813                 // this changes non-leafs into leafs if they have no children.
17814                 // it's not very rational behaviour..
17815                 
17816                 if(!this.wasLeaf && this.node.leaf){
17817                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17818                     delete this.c1;
17819                     delete this.c2;
17820                     this.wasLeaf = true;
17821                 }
17822             }
17823             var ecc = "x-tree-ec-icon "+cls;
17824             if(this.ecc != ecc){
17825                 this.ecNode.className = ecc;
17826                 this.ecc = ecc;
17827             }
17828         }
17829     },
17830
17831     getChildIndent : function(){
17832         if(!this.childIndent){
17833             var buf = [];
17834             var p = this.node;
17835             while(p){
17836                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17837                     if(!p.isLast()) {
17838                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17839                     } else {
17840                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17841                     }
17842                 }
17843                 p = p.parentNode;
17844             }
17845             this.childIndent = buf.join("");
17846         }
17847         return this.childIndent;
17848     },
17849
17850     renderIndent : function(){
17851         if(this.rendered){
17852             var indent = "";
17853             var p = this.node.parentNode;
17854             if(p){
17855                 indent = p.ui.getChildIndent();
17856             }
17857             if(this.indentMarkup != indent){ // don't rerender if not required
17858                 this.indentNode.innerHTML = indent;
17859                 this.indentMarkup = indent;
17860             }
17861             this.updateExpandIcon();
17862         }
17863     }
17864 };
17865
17866 Roo.tree.RootTreeNodeUI = function(){
17867     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17868 };
17869 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17870     render : function(){
17871         if(!this.rendered){
17872             var targetNode = this.node.ownerTree.innerCt.dom;
17873             this.node.expanded = true;
17874             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17875             this.wrap = this.ctNode = targetNode.firstChild;
17876         }
17877     },
17878     collapse : function(){
17879     },
17880     expand : function(){
17881     }
17882 });/*
17883  * Based on:
17884  * Ext JS Library 1.1.1
17885  * Copyright(c) 2006-2007, Ext JS, LLC.
17886  *
17887  * Originally Released Under LGPL - original licence link has changed is not relivant.
17888  *
17889  * Fork - LGPL
17890  * <script type="text/javascript">
17891  */
17892 /**
17893  * @class Roo.tree.TreeLoader
17894  * @extends Roo.util.Observable
17895  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17896  * nodes from a specified URL. The response must be a javascript Array definition
17897  * who's elements are node definition objects. eg:
17898  * <pre><code>
17899 {  success : true,
17900    data :      [
17901    
17902     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17903     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17904     ]
17905 }
17906
17907
17908 </code></pre>
17909  * <br><br>
17910  * The old style respose with just an array is still supported, but not recommended.
17911  * <br><br>
17912  *
17913  * A server request is sent, and child nodes are loaded only when a node is expanded.
17914  * The loading node's id is passed to the server under the parameter name "node" to
17915  * enable the server to produce the correct child nodes.
17916  * <br><br>
17917  * To pass extra parameters, an event handler may be attached to the "beforeload"
17918  * event, and the parameters specified in the TreeLoader's baseParams property:
17919  * <pre><code>
17920     myTreeLoader.on("beforeload", function(treeLoader, node) {
17921         this.baseParams.category = node.attributes.category;
17922     }, this);
17923 </code></pre><
17924  * This would pass an HTTP parameter called "category" to the server containing
17925  * the value of the Node's "category" attribute.
17926  * @constructor
17927  * Creates a new Treeloader.
17928  * @param {Object} config A config object containing config properties.
17929  */
17930 Roo.tree.TreeLoader = function(config){
17931     this.baseParams = {};
17932     this.requestMethod = "POST";
17933     Roo.apply(this, config);
17934
17935     this.addEvents({
17936     
17937         /**
17938          * @event beforeload
17939          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17940          * @param {Object} This TreeLoader object.
17941          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17942          * @param {Object} callback The callback function specified in the {@link #load} call.
17943          */
17944         beforeload : true,
17945         /**
17946          * @event load
17947          * Fires when the node has been successfuly loaded.
17948          * @param {Object} This TreeLoader object.
17949          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17950          * @param {Object} response The response object containing the data from the server.
17951          */
17952         load : true,
17953         /**
17954          * @event loadexception
17955          * Fires if the network request failed.
17956          * @param {Object} This TreeLoader object.
17957          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17958          * @param {Object} response The response object containing the data from the server.
17959          */
17960         loadexception : true,
17961         /**
17962          * @event create
17963          * Fires before a node is created, enabling you to return custom Node types 
17964          * @param {Object} This TreeLoader object.
17965          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17966          */
17967         create : true
17968     });
17969
17970     Roo.tree.TreeLoader.superclass.constructor.call(this);
17971 };
17972
17973 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17974     /**
17975     * @cfg {String} dataUrl The URL from which to request a Json string which
17976     * specifies an array of node definition object representing the child nodes
17977     * to be loaded.
17978     */
17979     /**
17980     * @cfg {String} requestMethod either GET or POST
17981     * defaults to POST (due to BC)
17982     * to be loaded.
17983     */
17984     /**
17985     * @cfg {Object} baseParams (optional) An object containing properties which
17986     * specify HTTP parameters to be passed to each request for child nodes.
17987     */
17988     /**
17989     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17990     * created by this loader. If the attributes sent by the server have an attribute in this object,
17991     * they take priority.
17992     */
17993     /**
17994     * @cfg {Object} uiProviders (optional) An object containing properties which
17995     * 
17996     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17997     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17998     * <i>uiProvider</i> attribute of a returned child node is a string rather
17999     * than a reference to a TreeNodeUI implementation, this that string value
18000     * is used as a property name in the uiProviders object. You can define the provider named
18001     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18002     */
18003     uiProviders : {},
18004
18005     /**
18006     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18007     * child nodes before loading.
18008     */
18009     clearOnLoad : true,
18010
18011     /**
18012     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18013     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18014     * Grid query { data : [ .....] }
18015     */
18016     
18017     root : false,
18018      /**
18019     * @cfg {String} queryParam (optional) 
18020     * Name of the query as it will be passed on the querystring (defaults to 'node')
18021     * eg. the request will be ?node=[id]
18022     */
18023     
18024     
18025     queryParam: false,
18026     
18027     /**
18028      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18029      * This is called automatically when a node is expanded, but may be used to reload
18030      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18031      * @param {Roo.tree.TreeNode} node
18032      * @param {Function} callback
18033      */
18034     load : function(node, callback){
18035         if(this.clearOnLoad){
18036             while(node.firstChild){
18037                 node.removeChild(node.firstChild);
18038             }
18039         }
18040         if(node.attributes.children){ // preloaded json children
18041             var cs = node.attributes.children;
18042             for(var i = 0, len = cs.length; i < len; i++){
18043                 node.appendChild(this.createNode(cs[i]));
18044             }
18045             if(typeof callback == "function"){
18046                 callback();
18047             }
18048         }else if(this.dataUrl){
18049             this.requestData(node, callback);
18050         }
18051     },
18052
18053     getParams: function(node){
18054         var buf = [], bp = this.baseParams;
18055         for(var key in bp){
18056             if(typeof bp[key] != "function"){
18057                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18058             }
18059         }
18060         var n = this.queryParam === false ? 'node' : this.queryParam;
18061         buf.push(n + "=", encodeURIComponent(node.id));
18062         return buf.join("");
18063     },
18064
18065     requestData : function(node, callback){
18066         if(this.fireEvent("beforeload", this, node, callback) !== false){
18067             this.transId = Roo.Ajax.request({
18068                 method:this.requestMethod,
18069                 url: this.dataUrl||this.url,
18070                 success: this.handleResponse,
18071                 failure: this.handleFailure,
18072                 scope: this,
18073                 argument: {callback: callback, node: node},
18074                 params: this.getParams(node)
18075             });
18076         }else{
18077             // if the load is cancelled, make sure we notify
18078             // the node that we are done
18079             if(typeof callback == "function"){
18080                 callback();
18081             }
18082         }
18083     },
18084
18085     isLoading : function(){
18086         return this.transId ? true : false;
18087     },
18088
18089     abort : function(){
18090         if(this.isLoading()){
18091             Roo.Ajax.abort(this.transId);
18092         }
18093     },
18094
18095     // private
18096     createNode : function(attr)
18097     {
18098         // apply baseAttrs, nice idea Corey!
18099         if(this.baseAttrs){
18100             Roo.applyIf(attr, this.baseAttrs);
18101         }
18102         if(this.applyLoader !== false){
18103             attr.loader = this;
18104         }
18105         // uiProvider = depreciated..
18106         
18107         if(typeof(attr.uiProvider) == 'string'){
18108            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18109                 /**  eval:var:attr */ eval(attr.uiProvider);
18110         }
18111         if(typeof(this.uiProviders['default']) != 'undefined') {
18112             attr.uiProvider = this.uiProviders['default'];
18113         }
18114         
18115         this.fireEvent('create', this, attr);
18116         
18117         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18118         return(attr.leaf ?
18119                         new Roo.tree.TreeNode(attr) :
18120                         new Roo.tree.AsyncTreeNode(attr));
18121     },
18122
18123     processResponse : function(response, node, callback)
18124     {
18125         var json = response.responseText;
18126         try {
18127             
18128             var o = Roo.decode(json);
18129             
18130             if (this.root === false && typeof(o.success) != undefined) {
18131                 this.root = 'data'; // the default behaviour for list like data..
18132                 }
18133                 
18134             if (this.root !== false &&  !o.success) {
18135                 // it's a failure condition.
18136                 var a = response.argument;
18137                 this.fireEvent("loadexception", this, a.node, response);
18138                 Roo.log("Load failed - should have a handler really");
18139                 return;
18140             }
18141             
18142             
18143             
18144             if (this.root !== false) {
18145                  o = o[this.root];
18146             }
18147             
18148             for(var i = 0, len = o.length; i < len; i++){
18149                 var n = this.createNode(o[i]);
18150                 if(n){
18151                     node.appendChild(n);
18152                 }
18153             }
18154             if(typeof callback == "function"){
18155                 callback(this, node);
18156             }
18157         }catch(e){
18158             this.handleFailure(response);
18159         }
18160     },
18161
18162     handleResponse : function(response){
18163         this.transId = false;
18164         var a = response.argument;
18165         this.processResponse(response, a.node, a.callback);
18166         this.fireEvent("load", this, a.node, response);
18167     },
18168
18169     handleFailure : function(response)
18170     {
18171         // should handle failure better..
18172         this.transId = false;
18173         var a = response.argument;
18174         this.fireEvent("loadexception", this, a.node, response);
18175         if(typeof a.callback == "function"){
18176             a.callback(this, a.node);
18177         }
18178     }
18179 });/*
18180  * Based on:
18181  * Ext JS Library 1.1.1
18182  * Copyright(c) 2006-2007, Ext JS, LLC.
18183  *
18184  * Originally Released Under LGPL - original licence link has changed is not relivant.
18185  *
18186  * Fork - LGPL
18187  * <script type="text/javascript">
18188  */
18189
18190 /**
18191 * @class Roo.tree.TreeFilter
18192 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18193 * @param {TreePanel} tree
18194 * @param {Object} config (optional)
18195  */
18196 Roo.tree.TreeFilter = function(tree, config){
18197     this.tree = tree;
18198     this.filtered = {};
18199     Roo.apply(this, config);
18200 };
18201
18202 Roo.tree.TreeFilter.prototype = {
18203     clearBlank:false,
18204     reverse:false,
18205     autoClear:false,
18206     remove:false,
18207
18208      /**
18209      * Filter the data by a specific attribute.
18210      * @param {String/RegExp} value Either string that the attribute value
18211      * should start with or a RegExp to test against the attribute
18212      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18213      * @param {TreeNode} startNode (optional) The node to start the filter at.
18214      */
18215     filter : function(value, attr, startNode){
18216         attr = attr || "text";
18217         var f;
18218         if(typeof value == "string"){
18219             var vlen = value.length;
18220             // auto clear empty filter
18221             if(vlen == 0 && this.clearBlank){
18222                 this.clear();
18223                 return;
18224             }
18225             value = value.toLowerCase();
18226             f = function(n){
18227                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18228             };
18229         }else if(value.exec){ // regex?
18230             f = function(n){
18231                 return value.test(n.attributes[attr]);
18232             };
18233         }else{
18234             throw 'Illegal filter type, must be string or regex';
18235         }
18236         this.filterBy(f, null, startNode);
18237         },
18238
18239     /**
18240      * Filter by a function. The passed function will be called with each
18241      * node in the tree (or from the startNode). If the function returns true, the node is kept
18242      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18243      * @param {Function} fn The filter function
18244      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18245      */
18246     filterBy : function(fn, scope, startNode){
18247         startNode = startNode || this.tree.root;
18248         if(this.autoClear){
18249             this.clear();
18250         }
18251         var af = this.filtered, rv = this.reverse;
18252         var f = function(n){
18253             if(n == startNode){
18254                 return true;
18255             }
18256             if(af[n.id]){
18257                 return false;
18258             }
18259             var m = fn.call(scope || n, n);
18260             if(!m || rv){
18261                 af[n.id] = n;
18262                 n.ui.hide();
18263                 return false;
18264             }
18265             return true;
18266         };
18267         startNode.cascade(f);
18268         if(this.remove){
18269            for(var id in af){
18270                if(typeof id != "function"){
18271                    var n = af[id];
18272                    if(n && n.parentNode){
18273                        n.parentNode.removeChild(n);
18274                    }
18275                }
18276            }
18277         }
18278     },
18279
18280     /**
18281      * Clears the current filter. Note: with the "remove" option
18282      * set a filter cannot be cleared.
18283      */
18284     clear : function(){
18285         var t = this.tree;
18286         var af = this.filtered;
18287         for(var id in af){
18288             if(typeof id != "function"){
18289                 var n = af[id];
18290                 if(n){
18291                     n.ui.show();
18292                 }
18293             }
18294         }
18295         this.filtered = {};
18296     }
18297 };
18298 /*
18299  * Based on:
18300  * Ext JS Library 1.1.1
18301  * Copyright(c) 2006-2007, Ext JS, LLC.
18302  *
18303  * Originally Released Under LGPL - original licence link has changed is not relivant.
18304  *
18305  * Fork - LGPL
18306  * <script type="text/javascript">
18307  */
18308  
18309
18310 /**
18311  * @class Roo.tree.TreeSorter
18312  * Provides sorting of nodes in a TreePanel
18313  * 
18314  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18315  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18316  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18317  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18318  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18319  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18320  * @constructor
18321  * @param {TreePanel} tree
18322  * @param {Object} config
18323  */
18324 Roo.tree.TreeSorter = function(tree, config){
18325     Roo.apply(this, config);
18326     tree.on("beforechildrenrendered", this.doSort, this);
18327     tree.on("append", this.updateSort, this);
18328     tree.on("insert", this.updateSort, this);
18329     
18330     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18331     var p = this.property || "text";
18332     var sortType = this.sortType;
18333     var fs = this.folderSort;
18334     var cs = this.caseSensitive === true;
18335     var leafAttr = this.leafAttr || 'leaf';
18336
18337     this.sortFn = function(n1, n2){
18338         if(fs){
18339             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18340                 return 1;
18341             }
18342             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18343                 return -1;
18344             }
18345         }
18346         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18347         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18348         if(v1 < v2){
18349                         return dsc ? +1 : -1;
18350                 }else if(v1 > v2){
18351                         return dsc ? -1 : +1;
18352         }else{
18353                 return 0;
18354         }
18355     };
18356 };
18357
18358 Roo.tree.TreeSorter.prototype = {
18359     doSort : function(node){
18360         node.sort(this.sortFn);
18361     },
18362     
18363     compareNodes : function(n1, n2){
18364         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18365     },
18366     
18367     updateSort : function(tree, node){
18368         if(node.childrenRendered){
18369             this.doSort.defer(1, this, [node]);
18370         }
18371     }
18372 };/*
18373  * Based on:
18374  * Ext JS Library 1.1.1
18375  * Copyright(c) 2006-2007, Ext JS, LLC.
18376  *
18377  * Originally Released Under LGPL - original licence link has changed is not relivant.
18378  *
18379  * Fork - LGPL
18380  * <script type="text/javascript">
18381  */
18382
18383 if(Roo.dd.DropZone){
18384     
18385 Roo.tree.TreeDropZone = function(tree, config){
18386     this.allowParentInsert = false;
18387     this.allowContainerDrop = false;
18388     this.appendOnly = false;
18389     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18390     this.tree = tree;
18391     this.lastInsertClass = "x-tree-no-status";
18392     this.dragOverData = {};
18393 };
18394
18395 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18396     ddGroup : "TreeDD",
18397     scroll:  true,
18398     
18399     expandDelay : 1000,
18400     
18401     expandNode : function(node){
18402         if(node.hasChildNodes() && !node.isExpanded()){
18403             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18404         }
18405     },
18406     
18407     queueExpand : function(node){
18408         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18409     },
18410     
18411     cancelExpand : function(){
18412         if(this.expandProcId){
18413             clearTimeout(this.expandProcId);
18414             this.expandProcId = false;
18415         }
18416     },
18417     
18418     isValidDropPoint : function(n, pt, dd, e, data){
18419         if(!n || !data){ return false; }
18420         var targetNode = n.node;
18421         var dropNode = data.node;
18422         // default drop rules
18423         if(!(targetNode && targetNode.isTarget && pt)){
18424             return false;
18425         }
18426         if(pt == "append" && targetNode.allowChildren === false){
18427             return false;
18428         }
18429         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18430             return false;
18431         }
18432         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18433             return false;
18434         }
18435         // reuse the object
18436         var overEvent = this.dragOverData;
18437         overEvent.tree = this.tree;
18438         overEvent.target = targetNode;
18439         overEvent.data = data;
18440         overEvent.point = pt;
18441         overEvent.source = dd;
18442         overEvent.rawEvent = e;
18443         overEvent.dropNode = dropNode;
18444         overEvent.cancel = false;  
18445         var result = this.tree.fireEvent("nodedragover", overEvent);
18446         return overEvent.cancel === false && result !== false;
18447     },
18448     
18449     getDropPoint : function(e, n, dd)
18450     {
18451         var tn = n.node;
18452         if(tn.isRoot){
18453             return tn.allowChildren !== false ? "append" : false; // always append for root
18454         }
18455         var dragEl = n.ddel;
18456         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18457         var y = Roo.lib.Event.getPageY(e);
18458         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18459         
18460         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18461         var noAppend = tn.allowChildren === false;
18462         if(this.appendOnly || tn.parentNode.allowChildren === false){
18463             return noAppend ? false : "append";
18464         }
18465         var noBelow = false;
18466         if(!this.allowParentInsert){
18467             noBelow = tn.hasChildNodes() && tn.isExpanded();
18468         }
18469         var q = (b - t) / (noAppend ? 2 : 3);
18470         if(y >= t && y < (t + q)){
18471             return "above";
18472         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18473             return "below";
18474         }else{
18475             return "append";
18476         }
18477     },
18478     
18479     onNodeEnter : function(n, dd, e, data)
18480     {
18481         this.cancelExpand();
18482     },
18483     
18484     onNodeOver : function(n, dd, e, data)
18485     {
18486        
18487         var pt = this.getDropPoint(e, n, dd);
18488         var node = n.node;
18489         
18490         // auto node expand check
18491         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18492             this.queueExpand(node);
18493         }else if(pt != "append"){
18494             this.cancelExpand();
18495         }
18496         
18497         // set the insert point style on the target node
18498         var returnCls = this.dropNotAllowed;
18499         if(this.isValidDropPoint(n, pt, dd, e, data)){
18500            if(pt){
18501                var el = n.ddel;
18502                var cls;
18503                if(pt == "above"){
18504                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18505                    cls = "x-tree-drag-insert-above";
18506                }else if(pt == "below"){
18507                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18508                    cls = "x-tree-drag-insert-below";
18509                }else{
18510                    returnCls = "x-tree-drop-ok-append";
18511                    cls = "x-tree-drag-append";
18512                }
18513                if(this.lastInsertClass != cls){
18514                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18515                    this.lastInsertClass = cls;
18516                }
18517            }
18518        }
18519        return returnCls;
18520     },
18521     
18522     onNodeOut : function(n, dd, e, data){
18523         
18524         this.cancelExpand();
18525         this.removeDropIndicators(n);
18526     },
18527     
18528     onNodeDrop : function(n, dd, e, data){
18529         var point = this.getDropPoint(e, n, dd);
18530         var targetNode = n.node;
18531         targetNode.ui.startDrop();
18532         if(!this.isValidDropPoint(n, point, dd, e, data)){
18533             targetNode.ui.endDrop();
18534             return false;
18535         }
18536         // first try to find the drop node
18537         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18538         var dropEvent = {
18539             tree : this.tree,
18540             target: targetNode,
18541             data: data,
18542             point: point,
18543             source: dd,
18544             rawEvent: e,
18545             dropNode: dropNode,
18546             cancel: !dropNode   
18547         };
18548         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18549         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18550             targetNode.ui.endDrop();
18551             return false;
18552         }
18553         // allow target changing
18554         targetNode = dropEvent.target;
18555         if(point == "append" && !targetNode.isExpanded()){
18556             targetNode.expand(false, null, function(){
18557                 this.completeDrop(dropEvent);
18558             }.createDelegate(this));
18559         }else{
18560             this.completeDrop(dropEvent);
18561         }
18562         return true;
18563     },
18564     
18565     completeDrop : function(de){
18566         var ns = de.dropNode, p = de.point, t = de.target;
18567         if(!(ns instanceof Array)){
18568             ns = [ns];
18569         }
18570         var n;
18571         for(var i = 0, len = ns.length; i < len; i++){
18572             n = ns[i];
18573             if(p == "above"){
18574                 t.parentNode.insertBefore(n, t);
18575             }else if(p == "below"){
18576                 t.parentNode.insertBefore(n, t.nextSibling);
18577             }else{
18578                 t.appendChild(n);
18579             }
18580         }
18581         n.ui.focus();
18582         if(this.tree.hlDrop){
18583             n.ui.highlight();
18584         }
18585         t.ui.endDrop();
18586         this.tree.fireEvent("nodedrop", de);
18587     },
18588     
18589     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18590         if(this.tree.hlDrop){
18591             dropNode.ui.focus();
18592             dropNode.ui.highlight();
18593         }
18594         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18595     },
18596     
18597     getTree : function(){
18598         return this.tree;
18599     },
18600     
18601     removeDropIndicators : function(n){
18602         if(n && n.ddel){
18603             var el = n.ddel;
18604             Roo.fly(el).removeClass([
18605                     "x-tree-drag-insert-above",
18606                     "x-tree-drag-insert-below",
18607                     "x-tree-drag-append"]);
18608             this.lastInsertClass = "_noclass";
18609         }
18610     },
18611     
18612     beforeDragDrop : function(target, e, id){
18613         this.cancelExpand();
18614         return true;
18615     },
18616     
18617     afterRepair : function(data){
18618         if(data && Roo.enableFx){
18619             data.node.ui.highlight();
18620         }
18621         this.hideProxy();
18622     } 
18623     
18624 });
18625
18626 }
18627 /*
18628  * Based on:
18629  * Ext JS Library 1.1.1
18630  * Copyright(c) 2006-2007, Ext JS, LLC.
18631  *
18632  * Originally Released Under LGPL - original licence link has changed is not relivant.
18633  *
18634  * Fork - LGPL
18635  * <script type="text/javascript">
18636  */
18637  
18638
18639 if(Roo.dd.DragZone){
18640 Roo.tree.TreeDragZone = function(tree, config){
18641     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18642     this.tree = tree;
18643 };
18644
18645 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18646     ddGroup : "TreeDD",
18647    
18648     onBeforeDrag : function(data, e){
18649         var n = data.node;
18650         return n && n.draggable && !n.disabled;
18651     },
18652      
18653     
18654     onInitDrag : function(e){
18655         var data = this.dragData;
18656         this.tree.getSelectionModel().select(data.node);
18657         this.proxy.update("");
18658         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18659         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18660     },
18661     
18662     getRepairXY : function(e, data){
18663         return data.node.ui.getDDRepairXY();
18664     },
18665     
18666     onEndDrag : function(data, e){
18667         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18668         
18669         
18670     },
18671     
18672     onValidDrop : function(dd, e, id){
18673         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18674         this.hideProxy();
18675     },
18676     
18677     beforeInvalidDrop : function(e, id){
18678         // this scrolls the original position back into view
18679         var sm = this.tree.getSelectionModel();
18680         sm.clearSelections();
18681         sm.select(this.dragData.node);
18682     }
18683 });
18684 }/*
18685  * Based on:
18686  * Ext JS Library 1.1.1
18687  * Copyright(c) 2006-2007, Ext JS, LLC.
18688  *
18689  * Originally Released Under LGPL - original licence link has changed is not relivant.
18690  *
18691  * Fork - LGPL
18692  * <script type="text/javascript">
18693  */
18694 /**
18695  * @class Roo.tree.TreeEditor
18696  * @extends Roo.Editor
18697  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18698  * as the editor field.
18699  * @constructor
18700  * @param {Object} config (used to be the tree panel.)
18701  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18702  * 
18703  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18704  * @cfg {Roo.form.TextField|Object} field The field configuration
18705  *
18706  * 
18707  */
18708 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18709     var tree = config;
18710     var field;
18711     if (oldconfig) { // old style..
18712         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18713     } else {
18714         // new style..
18715         tree = config.tree;
18716         config.field = config.field  || {};
18717         config.field.xtype = 'TextField';
18718         field = Roo.factory(config.field, Roo.form);
18719     }
18720     config = config || {};
18721     
18722     
18723     this.addEvents({
18724         /**
18725          * @event beforenodeedit
18726          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18727          * false from the handler of this event.
18728          * @param {Editor} this
18729          * @param {Roo.tree.Node} node 
18730          */
18731         "beforenodeedit" : true
18732     });
18733     
18734     //Roo.log(config);
18735     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18736
18737     this.tree = tree;
18738
18739     tree.on('beforeclick', this.beforeNodeClick, this);
18740     tree.getTreeEl().on('mousedown', this.hide, this);
18741     this.on('complete', this.updateNode, this);
18742     this.on('beforestartedit', this.fitToTree, this);
18743     this.on('startedit', this.bindScroll, this, {delay:10});
18744     this.on('specialkey', this.onSpecialKey, this);
18745 };
18746
18747 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18748     /**
18749      * @cfg {String} alignment
18750      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18751      */
18752     alignment: "l-l",
18753     // inherit
18754     autoSize: false,
18755     /**
18756      * @cfg {Boolean} hideEl
18757      * True to hide the bound element while the editor is displayed (defaults to false)
18758      */
18759     hideEl : false,
18760     /**
18761      * @cfg {String} cls
18762      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18763      */
18764     cls: "x-small-editor x-tree-editor",
18765     /**
18766      * @cfg {Boolean} shim
18767      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18768      */
18769     shim:false,
18770     // inherit
18771     shadow:"frame",
18772     /**
18773      * @cfg {Number} maxWidth
18774      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18775      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18776      * scroll and client offsets into account prior to each edit.
18777      */
18778     maxWidth: 250,
18779
18780     editDelay : 350,
18781
18782     // private
18783     fitToTree : function(ed, el){
18784         var td = this.tree.getTreeEl().dom, nd = el.dom;
18785         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18786             td.scrollLeft = nd.offsetLeft;
18787         }
18788         var w = Math.min(
18789                 this.maxWidth,
18790                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18791         this.setSize(w, '');
18792         
18793         return this.fireEvent('beforenodeedit', this, this.editNode);
18794         
18795     },
18796
18797     // private
18798     triggerEdit : function(node){
18799         this.completeEdit();
18800         this.editNode = node;
18801         this.startEdit(node.ui.textNode, node.text);
18802     },
18803
18804     // private
18805     bindScroll : function(){
18806         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18807     },
18808
18809     // private
18810     beforeNodeClick : function(node, e){
18811         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18812         this.lastClick = new Date();
18813         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18814             e.stopEvent();
18815             this.triggerEdit(node);
18816             return false;
18817         }
18818         return true;
18819     },
18820
18821     // private
18822     updateNode : function(ed, value){
18823         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18824         this.editNode.setText(value);
18825     },
18826
18827     // private
18828     onHide : function(){
18829         Roo.tree.TreeEditor.superclass.onHide.call(this);
18830         if(this.editNode){
18831             this.editNode.ui.focus();
18832         }
18833     },
18834
18835     // private
18836     onSpecialKey : function(field, e){
18837         var k = e.getKey();
18838         if(k == e.ESC){
18839             e.stopEvent();
18840             this.cancelEdit();
18841         }else if(k == e.ENTER && !e.hasModifier()){
18842             e.stopEvent();
18843             this.completeEdit();
18844         }
18845     }
18846 });//<Script type="text/javascript">
18847 /*
18848  * Based on:
18849  * Ext JS Library 1.1.1
18850  * Copyright(c) 2006-2007, Ext JS, LLC.
18851  *
18852  * Originally Released Under LGPL - original licence link has changed is not relivant.
18853  *
18854  * Fork - LGPL
18855  * <script type="text/javascript">
18856  */
18857  
18858 /**
18859  * Not documented??? - probably should be...
18860  */
18861
18862 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18863     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18864     
18865     renderElements : function(n, a, targetNode, bulkRender){
18866         //consel.log("renderElements?");
18867         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18868
18869         var t = n.getOwnerTree();
18870         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18871         
18872         var cols = t.columns;
18873         var bw = t.borderWidth;
18874         var c = cols[0];
18875         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18876          var cb = typeof a.checked == "boolean";
18877         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18878         var colcls = 'x-t-' + tid + '-c0';
18879         var buf = [
18880             '<li class="x-tree-node">',
18881             
18882                 
18883                 '<div class="x-tree-node-el ', a.cls,'">',
18884                     // extran...
18885                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18886                 
18887                 
18888                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18889                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18890                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18891                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18892                            (a.iconCls ? ' '+a.iconCls : ''),
18893                            '" unselectable="on" />',
18894                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18895                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18896                              
18897                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18898                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18899                             '<span unselectable="on" qtip="' + tx + '">',
18900                              tx,
18901                              '</span></a>' ,
18902                     '</div>',
18903                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18904                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18905                  ];
18906         for(var i = 1, len = cols.length; i < len; i++){
18907             c = cols[i];
18908             colcls = 'x-t-' + tid + '-c' +i;
18909             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18910             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18911                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18912                       "</div>");
18913          }
18914          
18915          buf.push(
18916             '</a>',
18917             '<div class="x-clear"></div></div>',
18918             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18919             "</li>");
18920         
18921         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18922             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18923                                 n.nextSibling.ui.getEl(), buf.join(""));
18924         }else{
18925             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18926         }
18927         var el = this.wrap.firstChild;
18928         this.elRow = el;
18929         this.elNode = el.firstChild;
18930         this.ranchor = el.childNodes[1];
18931         this.ctNode = this.wrap.childNodes[1];
18932         var cs = el.firstChild.childNodes;
18933         this.indentNode = cs[0];
18934         this.ecNode = cs[1];
18935         this.iconNode = cs[2];
18936         var index = 3;
18937         if(cb){
18938             this.checkbox = cs[3];
18939             index++;
18940         }
18941         this.anchor = cs[index];
18942         
18943         this.textNode = cs[index].firstChild;
18944         
18945         //el.on("click", this.onClick, this);
18946         //el.on("dblclick", this.onDblClick, this);
18947         
18948         
18949        // console.log(this);
18950     },
18951     initEvents : function(){
18952         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18953         
18954             
18955         var a = this.ranchor;
18956
18957         var el = Roo.get(a);
18958
18959         if(Roo.isOpera){ // opera render bug ignores the CSS
18960             el.setStyle("text-decoration", "none");
18961         }
18962
18963         el.on("click", this.onClick, this);
18964         el.on("dblclick", this.onDblClick, this);
18965         el.on("contextmenu", this.onContextMenu, this);
18966         
18967     },
18968     
18969     /*onSelectedChange : function(state){
18970         if(state){
18971             this.focus();
18972             this.addClass("x-tree-selected");
18973         }else{
18974             //this.blur();
18975             this.removeClass("x-tree-selected");
18976         }
18977     },*/
18978     addClass : function(cls){
18979         if(this.elRow){
18980             Roo.fly(this.elRow).addClass(cls);
18981         }
18982         
18983     },
18984     
18985     
18986     removeClass : function(cls){
18987         if(this.elRow){
18988             Roo.fly(this.elRow).removeClass(cls);
18989         }
18990     }
18991
18992     
18993     
18994 });//<Script type="text/javascript">
18995
18996 /*
18997  * Based on:
18998  * Ext JS Library 1.1.1
18999  * Copyright(c) 2006-2007, Ext JS, LLC.
19000  *
19001  * Originally Released Under LGPL - original licence link has changed is not relivant.
19002  *
19003  * Fork - LGPL
19004  * <script type="text/javascript">
19005  */
19006  
19007
19008 /**
19009  * @class Roo.tree.ColumnTree
19010  * @extends Roo.data.TreePanel
19011  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19012  * @cfg {int} borderWidth  compined right/left border allowance
19013  * @constructor
19014  * @param {String/HTMLElement/Element} el The container element
19015  * @param {Object} config
19016  */
19017 Roo.tree.ColumnTree =  function(el, config)
19018 {
19019    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19020    this.addEvents({
19021         /**
19022         * @event resize
19023         * Fire this event on a container when it resizes
19024         * @param {int} w Width
19025         * @param {int} h Height
19026         */
19027        "resize" : true
19028     });
19029     this.on('resize', this.onResize, this);
19030 };
19031
19032 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19033     //lines:false,
19034     
19035     
19036     borderWidth: Roo.isBorderBox ? 0 : 2, 
19037     headEls : false,
19038     
19039     render : function(){
19040         // add the header.....
19041        
19042         Roo.tree.ColumnTree.superclass.render.apply(this);
19043         
19044         this.el.addClass('x-column-tree');
19045         
19046         this.headers = this.el.createChild(
19047             {cls:'x-tree-headers'},this.innerCt.dom);
19048    
19049         var cols = this.columns, c;
19050         var totalWidth = 0;
19051         this.headEls = [];
19052         var  len = cols.length;
19053         for(var i = 0; i < len; i++){
19054              c = cols[i];
19055              totalWidth += c.width;
19056             this.headEls.push(this.headers.createChild({
19057                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19058                  cn: {
19059                      cls:'x-tree-hd-text',
19060                      html: c.header
19061                  },
19062                  style:'width:'+(c.width-this.borderWidth)+'px;'
19063              }));
19064         }
19065         this.headers.createChild({cls:'x-clear'});
19066         // prevent floats from wrapping when clipped
19067         this.headers.setWidth(totalWidth);
19068         //this.innerCt.setWidth(totalWidth);
19069         this.innerCt.setStyle({ overflow: 'auto' });
19070         this.onResize(this.width, this.height);
19071              
19072         
19073     },
19074     onResize : function(w,h)
19075     {
19076         this.height = h;
19077         this.width = w;
19078         // resize cols..
19079         this.innerCt.setWidth(this.width);
19080         this.innerCt.setHeight(this.height-20);
19081         
19082         // headers...
19083         var cols = this.columns, c;
19084         var totalWidth = 0;
19085         var expEl = false;
19086         var len = cols.length;
19087         for(var i = 0; i < len; i++){
19088             c = cols[i];
19089             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19090                 // it's the expander..
19091                 expEl  = this.headEls[i];
19092                 continue;
19093             }
19094             totalWidth += c.width;
19095             
19096         }
19097         if (expEl) {
19098             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19099         }
19100         this.headers.setWidth(w-20);
19101
19102         
19103         
19104         
19105     }
19106 });
19107 /*
19108  * Based on:
19109  * Ext JS Library 1.1.1
19110  * Copyright(c) 2006-2007, Ext JS, LLC.
19111  *
19112  * Originally Released Under LGPL - original licence link has changed is not relivant.
19113  *
19114  * Fork - LGPL
19115  * <script type="text/javascript">
19116  */
19117  
19118 /**
19119  * @class Roo.menu.Menu
19120  * @extends Roo.util.Observable
19121  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19122  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19123  * @constructor
19124  * Creates a new Menu
19125  * @param {Object} config Configuration options
19126  */
19127 Roo.menu.Menu = function(config){
19128     Roo.apply(this, config);
19129     this.id = this.id || Roo.id();
19130     this.addEvents({
19131         /**
19132          * @event beforeshow
19133          * Fires before this menu is displayed
19134          * @param {Roo.menu.Menu} this
19135          */
19136         beforeshow : true,
19137         /**
19138          * @event beforehide
19139          * Fires before this menu is hidden
19140          * @param {Roo.menu.Menu} this
19141          */
19142         beforehide : true,
19143         /**
19144          * @event show
19145          * Fires after this menu is displayed
19146          * @param {Roo.menu.Menu} this
19147          */
19148         show : true,
19149         /**
19150          * @event hide
19151          * Fires after this menu is hidden
19152          * @param {Roo.menu.Menu} this
19153          */
19154         hide : true,
19155         /**
19156          * @event click
19157          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19158          * @param {Roo.menu.Menu} this
19159          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19160          * @param {Roo.EventObject} e
19161          */
19162         click : true,
19163         /**
19164          * @event mouseover
19165          * Fires when the mouse is hovering over this menu
19166          * @param {Roo.menu.Menu} this
19167          * @param {Roo.EventObject} e
19168          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19169          */
19170         mouseover : true,
19171         /**
19172          * @event mouseout
19173          * Fires when the mouse exits this menu
19174          * @param {Roo.menu.Menu} this
19175          * @param {Roo.EventObject} e
19176          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19177          */
19178         mouseout : true,
19179         /**
19180          * @event itemclick
19181          * Fires when a menu item contained in this menu is clicked
19182          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19183          * @param {Roo.EventObject} e
19184          */
19185         itemclick: true
19186     });
19187     if (this.registerMenu) {
19188         Roo.menu.MenuMgr.register(this);
19189     }
19190     
19191     var mis = this.items;
19192     this.items = new Roo.util.MixedCollection();
19193     if(mis){
19194         this.add.apply(this, mis);
19195     }
19196 };
19197
19198 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19199     /**
19200      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19201      */
19202     minWidth : 120,
19203     /**
19204      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19205      * for bottom-right shadow (defaults to "sides")
19206      */
19207     shadow : "sides",
19208     /**
19209      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19210      * this menu (defaults to "tl-tr?")
19211      */
19212     subMenuAlign : "tl-tr?",
19213     /**
19214      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19215      * relative to its element of origin (defaults to "tl-bl?")
19216      */
19217     defaultAlign : "tl-bl?",
19218     /**
19219      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19220      */
19221     allowOtherMenus : false,
19222     /**
19223      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19224      */
19225     registerMenu : true,
19226
19227     hidden:true,
19228
19229     // private
19230     render : function(){
19231         if(this.el){
19232             return;
19233         }
19234         var el = this.el = new Roo.Layer({
19235             cls: "x-menu",
19236             shadow:this.shadow,
19237             constrain: false,
19238             parentEl: this.parentEl || document.body,
19239             zindex:15000
19240         });
19241
19242         this.keyNav = new Roo.menu.MenuNav(this);
19243
19244         if(this.plain){
19245             el.addClass("x-menu-plain");
19246         }
19247         if(this.cls){
19248             el.addClass(this.cls);
19249         }
19250         // generic focus element
19251         this.focusEl = el.createChild({
19252             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19253         });
19254         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19255         //disabling touch- as it's causing issues ..
19256         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19257         ul.on('click'   , this.onClick, this);
19258         
19259         
19260         ul.on("mouseover", this.onMouseOver, this);
19261         ul.on("mouseout", this.onMouseOut, this);
19262         this.items.each(function(item){
19263             if (item.hidden) {
19264                 return;
19265             }
19266             
19267             var li = document.createElement("li");
19268             li.className = "x-menu-list-item";
19269             ul.dom.appendChild(li);
19270             item.render(li, this);
19271         }, this);
19272         this.ul = ul;
19273         this.autoWidth();
19274     },
19275
19276     // private
19277     autoWidth : function(){
19278         var el = this.el, ul = this.ul;
19279         if(!el){
19280             return;
19281         }
19282         var w = this.width;
19283         if(w){
19284             el.setWidth(w);
19285         }else if(Roo.isIE){
19286             el.setWidth(this.minWidth);
19287             var t = el.dom.offsetWidth; // force recalc
19288             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19289         }
19290     },
19291
19292     // private
19293     delayAutoWidth : function(){
19294         if(this.rendered){
19295             if(!this.awTask){
19296                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19297             }
19298             this.awTask.delay(20);
19299         }
19300     },
19301
19302     // private
19303     findTargetItem : function(e){
19304         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19305         if(t && t.menuItemId){
19306             return this.items.get(t.menuItemId);
19307         }
19308     },
19309
19310     // private
19311     onClick : function(e){
19312         Roo.log("menu.onClick");
19313         var t = this.findTargetItem(e);
19314         if(!t){
19315             return;
19316         }
19317         Roo.log(e);
19318         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19319             if(t == this.activeItem && t.shouldDeactivate(e)){
19320                 this.activeItem.deactivate();
19321                 delete this.activeItem;
19322                 return;
19323             }
19324             if(t.canActivate){
19325                 this.setActiveItem(t, true);
19326             }
19327             return;
19328             
19329             
19330         }
19331         
19332         t.onClick(e);
19333         this.fireEvent("click", this, t, e);
19334     },
19335
19336     // private
19337     setActiveItem : function(item, autoExpand){
19338         if(item != this.activeItem){
19339             if(this.activeItem){
19340                 this.activeItem.deactivate();
19341             }
19342             this.activeItem = item;
19343             item.activate(autoExpand);
19344         }else if(autoExpand){
19345             item.expandMenu();
19346         }
19347     },
19348
19349     // private
19350     tryActivate : function(start, step){
19351         var items = this.items;
19352         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19353             var item = items.get(i);
19354             if(!item.disabled && item.canActivate){
19355                 this.setActiveItem(item, false);
19356                 return item;
19357             }
19358         }
19359         return false;
19360     },
19361
19362     // private
19363     onMouseOver : function(e){
19364         var t;
19365         if(t = this.findTargetItem(e)){
19366             if(t.canActivate && !t.disabled){
19367                 this.setActiveItem(t, true);
19368             }
19369         }
19370         this.fireEvent("mouseover", this, e, t);
19371     },
19372
19373     // private
19374     onMouseOut : function(e){
19375         var t;
19376         if(t = this.findTargetItem(e)){
19377             if(t == this.activeItem && t.shouldDeactivate(e)){
19378                 this.activeItem.deactivate();
19379                 delete this.activeItem;
19380             }
19381         }
19382         this.fireEvent("mouseout", this, e, t);
19383     },
19384
19385     /**
19386      * Read-only.  Returns true if the menu is currently displayed, else false.
19387      * @type Boolean
19388      */
19389     isVisible : function(){
19390         return this.el && !this.hidden;
19391     },
19392
19393     /**
19394      * Displays this menu relative to another element
19395      * @param {String/HTMLElement/Roo.Element} element The element to align to
19396      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19397      * the element (defaults to this.defaultAlign)
19398      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19399      */
19400     show : function(el, pos, parentMenu){
19401         this.parentMenu = parentMenu;
19402         if(!this.el){
19403             this.render();
19404         }
19405         this.fireEvent("beforeshow", this);
19406         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19407     },
19408
19409     /**
19410      * Displays this menu at a specific xy position
19411      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19412      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19413      */
19414     showAt : function(xy, parentMenu, /* private: */_e){
19415         this.parentMenu = parentMenu;
19416         if(!this.el){
19417             this.render();
19418         }
19419         if(_e !== false){
19420             this.fireEvent("beforeshow", this);
19421             xy = this.el.adjustForConstraints(xy);
19422         }
19423         this.el.setXY(xy);
19424         this.el.show();
19425         this.hidden = false;
19426         this.focus();
19427         this.fireEvent("show", this);
19428     },
19429
19430     focus : function(){
19431         if(!this.hidden){
19432             this.doFocus.defer(50, this);
19433         }
19434     },
19435
19436     doFocus : function(){
19437         if(!this.hidden){
19438             this.focusEl.focus();
19439         }
19440     },
19441
19442     /**
19443      * Hides this menu and optionally all parent menus
19444      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19445      */
19446     hide : function(deep){
19447         if(this.el && this.isVisible()){
19448             this.fireEvent("beforehide", this);
19449             if(this.activeItem){
19450                 this.activeItem.deactivate();
19451                 this.activeItem = null;
19452             }
19453             this.el.hide();
19454             this.hidden = true;
19455             this.fireEvent("hide", this);
19456         }
19457         if(deep === true && this.parentMenu){
19458             this.parentMenu.hide(true);
19459         }
19460     },
19461
19462     /**
19463      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19464      * Any of the following are valid:
19465      * <ul>
19466      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19467      * <li>An HTMLElement object which will be converted to a menu item</li>
19468      * <li>A menu item config object that will be created as a new menu item</li>
19469      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19470      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19471      * </ul>
19472      * Usage:
19473      * <pre><code>
19474 // Create the menu
19475 var menu = new Roo.menu.Menu();
19476
19477 // Create a menu item to add by reference
19478 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19479
19480 // Add a bunch of items at once using different methods.
19481 // Only the last item added will be returned.
19482 var item = menu.add(
19483     menuItem,                // add existing item by ref
19484     'Dynamic Item',          // new TextItem
19485     '-',                     // new separator
19486     { text: 'Config Item' }  // new item by config
19487 );
19488 </code></pre>
19489      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19490      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19491      */
19492     add : function(){
19493         var a = arguments, l = a.length, item;
19494         for(var i = 0; i < l; i++){
19495             var el = a[i];
19496             if ((typeof(el) == "object") && el.xtype && el.xns) {
19497                 el = Roo.factory(el, Roo.menu);
19498             }
19499             
19500             if(el.render){ // some kind of Item
19501                 item = this.addItem(el);
19502             }else if(typeof el == "string"){ // string
19503                 if(el == "separator" || el == "-"){
19504                     item = this.addSeparator();
19505                 }else{
19506                     item = this.addText(el);
19507                 }
19508             }else if(el.tagName || el.el){ // element
19509                 item = this.addElement(el);
19510             }else if(typeof el == "object"){ // must be menu item config?
19511                 item = this.addMenuItem(el);
19512             }
19513         }
19514         return item;
19515     },
19516
19517     /**
19518      * Returns this menu's underlying {@link Roo.Element} object
19519      * @return {Roo.Element} The element
19520      */
19521     getEl : function(){
19522         if(!this.el){
19523             this.render();
19524         }
19525         return this.el;
19526     },
19527
19528     /**
19529      * Adds a separator bar to the menu
19530      * @return {Roo.menu.Item} The menu item that was added
19531      */
19532     addSeparator : function(){
19533         return this.addItem(new Roo.menu.Separator());
19534     },
19535
19536     /**
19537      * Adds an {@link Roo.Element} object to the menu
19538      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19539      * @return {Roo.menu.Item} The menu item that was added
19540      */
19541     addElement : function(el){
19542         return this.addItem(new Roo.menu.BaseItem(el));
19543     },
19544
19545     /**
19546      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19547      * @param {Roo.menu.Item} item The menu item to add
19548      * @return {Roo.menu.Item} The menu item that was added
19549      */
19550     addItem : function(item){
19551         this.items.add(item);
19552         if(this.ul){
19553             var li = document.createElement("li");
19554             li.className = "x-menu-list-item";
19555             this.ul.dom.appendChild(li);
19556             item.render(li, this);
19557             this.delayAutoWidth();
19558         }
19559         return item;
19560     },
19561
19562     /**
19563      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19564      * @param {Object} config A MenuItem config object
19565      * @return {Roo.menu.Item} The menu item that was added
19566      */
19567     addMenuItem : function(config){
19568         if(!(config instanceof Roo.menu.Item)){
19569             if(typeof config.checked == "boolean"){ // must be check menu item config?
19570                 config = new Roo.menu.CheckItem(config);
19571             }else{
19572                 config = new Roo.menu.Item(config);
19573             }
19574         }
19575         return this.addItem(config);
19576     },
19577
19578     /**
19579      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19580      * @param {String} text The text to display in the menu item
19581      * @return {Roo.menu.Item} The menu item that was added
19582      */
19583     addText : function(text){
19584         return this.addItem(new Roo.menu.TextItem({ text : text }));
19585     },
19586
19587     /**
19588      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19589      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19590      * @param {Roo.menu.Item} item The menu item to add
19591      * @return {Roo.menu.Item} The menu item that was added
19592      */
19593     insert : function(index, item){
19594         this.items.insert(index, item);
19595         if(this.ul){
19596             var li = document.createElement("li");
19597             li.className = "x-menu-list-item";
19598             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19599             item.render(li, this);
19600             this.delayAutoWidth();
19601         }
19602         return item;
19603     },
19604
19605     /**
19606      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19607      * @param {Roo.menu.Item} item The menu item to remove
19608      */
19609     remove : function(item){
19610         this.items.removeKey(item.id);
19611         item.destroy();
19612     },
19613
19614     /**
19615      * Removes and destroys all items in the menu
19616      */
19617     removeAll : function(){
19618         var f;
19619         while(f = this.items.first()){
19620             this.remove(f);
19621         }
19622     }
19623 });
19624
19625 // MenuNav is a private utility class used internally by the Menu
19626 Roo.menu.MenuNav = function(menu){
19627     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19628     this.scope = this.menu = menu;
19629 };
19630
19631 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19632     doRelay : function(e, h){
19633         var k = e.getKey();
19634         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19635             this.menu.tryActivate(0, 1);
19636             return false;
19637         }
19638         return h.call(this.scope || this, e, this.menu);
19639     },
19640
19641     up : function(e, m){
19642         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19643             m.tryActivate(m.items.length-1, -1);
19644         }
19645     },
19646
19647     down : function(e, m){
19648         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19649             m.tryActivate(0, 1);
19650         }
19651     },
19652
19653     right : function(e, m){
19654         if(m.activeItem){
19655             m.activeItem.expandMenu(true);
19656         }
19657     },
19658
19659     left : function(e, m){
19660         m.hide();
19661         if(m.parentMenu && m.parentMenu.activeItem){
19662             m.parentMenu.activeItem.activate();
19663         }
19664     },
19665
19666     enter : function(e, m){
19667         if(m.activeItem){
19668             e.stopPropagation();
19669             m.activeItem.onClick(e);
19670             m.fireEvent("click", this, m.activeItem);
19671             return true;
19672         }
19673     }
19674 });/*
19675  * Based on:
19676  * Ext JS Library 1.1.1
19677  * Copyright(c) 2006-2007, Ext JS, LLC.
19678  *
19679  * Originally Released Under LGPL - original licence link has changed is not relivant.
19680  *
19681  * Fork - LGPL
19682  * <script type="text/javascript">
19683  */
19684  
19685 /**
19686  * @class Roo.menu.MenuMgr
19687  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19688  * @singleton
19689  */
19690 Roo.menu.MenuMgr = function(){
19691    var menus, active, groups = {}, attached = false, lastShow = new Date();
19692
19693    // private - called when first menu is created
19694    function init(){
19695        menus = {};
19696        active = new Roo.util.MixedCollection();
19697        Roo.get(document).addKeyListener(27, function(){
19698            if(active.length > 0){
19699                hideAll();
19700            }
19701        });
19702    }
19703
19704    // private
19705    function hideAll(){
19706        if(active && active.length > 0){
19707            var c = active.clone();
19708            c.each(function(m){
19709                m.hide();
19710            });
19711        }
19712    }
19713
19714    // private
19715    function onHide(m){
19716        active.remove(m);
19717        if(active.length < 1){
19718            Roo.get(document).un("mousedown", onMouseDown);
19719            attached = false;
19720        }
19721    }
19722
19723    // private
19724    function onShow(m){
19725        var last = active.last();
19726        lastShow = new Date();
19727        active.add(m);
19728        if(!attached){
19729            Roo.get(document).on("mousedown", onMouseDown);
19730            attached = true;
19731        }
19732        if(m.parentMenu){
19733           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19734           m.parentMenu.activeChild = m;
19735        }else if(last && last.isVisible()){
19736           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19737        }
19738    }
19739
19740    // private
19741    function onBeforeHide(m){
19742        if(m.activeChild){
19743            m.activeChild.hide();
19744        }
19745        if(m.autoHideTimer){
19746            clearTimeout(m.autoHideTimer);
19747            delete m.autoHideTimer;
19748        }
19749    }
19750
19751    // private
19752    function onBeforeShow(m){
19753        var pm = m.parentMenu;
19754        if(!pm && !m.allowOtherMenus){
19755            hideAll();
19756        }else if(pm && pm.activeChild && active != m){
19757            pm.activeChild.hide();
19758        }
19759    }
19760
19761    // private
19762    function onMouseDown(e){
19763        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19764            hideAll();
19765        }
19766    }
19767
19768    // private
19769    function onBeforeCheck(mi, state){
19770        if(state){
19771            var g = groups[mi.group];
19772            for(var i = 0, l = g.length; i < l; i++){
19773                if(g[i] != mi){
19774                    g[i].setChecked(false);
19775                }
19776            }
19777        }
19778    }
19779
19780    return {
19781
19782        /**
19783         * Hides all menus that are currently visible
19784         */
19785        hideAll : function(){
19786             hideAll();  
19787        },
19788
19789        // private
19790        register : function(menu){
19791            if(!menus){
19792                init();
19793            }
19794            menus[menu.id] = menu;
19795            menu.on("beforehide", onBeforeHide);
19796            menu.on("hide", onHide);
19797            menu.on("beforeshow", onBeforeShow);
19798            menu.on("show", onShow);
19799            var g = menu.group;
19800            if(g && menu.events["checkchange"]){
19801                if(!groups[g]){
19802                    groups[g] = [];
19803                }
19804                groups[g].push(menu);
19805                menu.on("checkchange", onCheck);
19806            }
19807        },
19808
19809         /**
19810          * Returns a {@link Roo.menu.Menu} object
19811          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19812          * be used to generate and return a new Menu instance.
19813          */
19814        get : function(menu){
19815            if(typeof menu == "string"){ // menu id
19816                return menus[menu];
19817            }else if(menu.events){  // menu instance
19818                return menu;
19819            }else if(typeof menu.length == 'number'){ // array of menu items?
19820                return new Roo.menu.Menu({items:menu});
19821            }else{ // otherwise, must be a config
19822                return new Roo.menu.Menu(menu);
19823            }
19824        },
19825
19826        // private
19827        unregister : function(menu){
19828            delete menus[menu.id];
19829            menu.un("beforehide", onBeforeHide);
19830            menu.un("hide", onHide);
19831            menu.un("beforeshow", onBeforeShow);
19832            menu.un("show", onShow);
19833            var g = menu.group;
19834            if(g && menu.events["checkchange"]){
19835                groups[g].remove(menu);
19836                menu.un("checkchange", onCheck);
19837            }
19838        },
19839
19840        // private
19841        registerCheckable : function(menuItem){
19842            var g = menuItem.group;
19843            if(g){
19844                if(!groups[g]){
19845                    groups[g] = [];
19846                }
19847                groups[g].push(menuItem);
19848                menuItem.on("beforecheckchange", onBeforeCheck);
19849            }
19850        },
19851
19852        // private
19853        unregisterCheckable : function(menuItem){
19854            var g = menuItem.group;
19855            if(g){
19856                groups[g].remove(menuItem);
19857                menuItem.un("beforecheckchange", onBeforeCheck);
19858            }
19859        }
19860    };
19861 }();/*
19862  * Based on:
19863  * Ext JS Library 1.1.1
19864  * Copyright(c) 2006-2007, Ext JS, LLC.
19865  *
19866  * Originally Released Under LGPL - original licence link has changed is not relivant.
19867  *
19868  * Fork - LGPL
19869  * <script type="text/javascript">
19870  */
19871  
19872
19873 /**
19874  * @class Roo.menu.BaseItem
19875  * @extends Roo.Component
19876  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19877  * management and base configuration options shared by all menu components.
19878  * @constructor
19879  * Creates a new BaseItem
19880  * @param {Object} config Configuration options
19881  */
19882 Roo.menu.BaseItem = function(config){
19883     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19884
19885     this.addEvents({
19886         /**
19887          * @event click
19888          * Fires when this item is clicked
19889          * @param {Roo.menu.BaseItem} this
19890          * @param {Roo.EventObject} e
19891          */
19892         click: true,
19893         /**
19894          * @event activate
19895          * Fires when this item is activated
19896          * @param {Roo.menu.BaseItem} this
19897          */
19898         activate : true,
19899         /**
19900          * @event deactivate
19901          * Fires when this item is deactivated
19902          * @param {Roo.menu.BaseItem} this
19903          */
19904         deactivate : true
19905     });
19906
19907     if(this.handler){
19908         this.on("click", this.handler, this.scope, true);
19909     }
19910 };
19911
19912 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19913     /**
19914      * @cfg {Function} handler
19915      * A function that will handle the click event of this menu item (defaults to undefined)
19916      */
19917     /**
19918      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19919      */
19920     canActivate : false,
19921     
19922      /**
19923      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19924      */
19925     hidden: false,
19926     
19927     /**
19928      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19929      */
19930     activeClass : "x-menu-item-active",
19931     /**
19932      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19933      */
19934     hideOnClick : true,
19935     /**
19936      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19937      */
19938     hideDelay : 100,
19939
19940     // private
19941     ctype: "Roo.menu.BaseItem",
19942
19943     // private
19944     actionMode : "container",
19945
19946     // private
19947     render : function(container, parentMenu){
19948         this.parentMenu = parentMenu;
19949         Roo.menu.BaseItem.superclass.render.call(this, container);
19950         this.container.menuItemId = this.id;
19951     },
19952
19953     // private
19954     onRender : function(container, position){
19955         this.el = Roo.get(this.el);
19956         container.dom.appendChild(this.el.dom);
19957     },
19958
19959     // private
19960     onClick : function(e){
19961         if(!this.disabled && this.fireEvent("click", this, e) !== false
19962                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19963             this.handleClick(e);
19964         }else{
19965             e.stopEvent();
19966         }
19967     },
19968
19969     // private
19970     activate : function(){
19971         if(this.disabled){
19972             return false;
19973         }
19974         var li = this.container;
19975         li.addClass(this.activeClass);
19976         this.region = li.getRegion().adjust(2, 2, -2, -2);
19977         this.fireEvent("activate", this);
19978         return true;
19979     },
19980
19981     // private
19982     deactivate : function(){
19983         this.container.removeClass(this.activeClass);
19984         this.fireEvent("deactivate", this);
19985     },
19986
19987     // private
19988     shouldDeactivate : function(e){
19989         return !this.region || !this.region.contains(e.getPoint());
19990     },
19991
19992     // private
19993     handleClick : function(e){
19994         if(this.hideOnClick){
19995             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19996         }
19997     },
19998
19999     // private
20000     expandMenu : function(autoActivate){
20001         // do nothing
20002     },
20003
20004     // private
20005     hideMenu : function(){
20006         // do nothing
20007     }
20008 });/*
20009  * Based on:
20010  * Ext JS Library 1.1.1
20011  * Copyright(c) 2006-2007, Ext JS, LLC.
20012  *
20013  * Originally Released Under LGPL - original licence link has changed is not relivant.
20014  *
20015  * Fork - LGPL
20016  * <script type="text/javascript">
20017  */
20018  
20019 /**
20020  * @class Roo.menu.Adapter
20021  * @extends Roo.menu.BaseItem
20022  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20023  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20024  * @constructor
20025  * Creates a new Adapter
20026  * @param {Object} config Configuration options
20027  */
20028 Roo.menu.Adapter = function(component, config){
20029     Roo.menu.Adapter.superclass.constructor.call(this, config);
20030     this.component = component;
20031 };
20032 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20033     // private
20034     canActivate : true,
20035
20036     // private
20037     onRender : function(container, position){
20038         this.component.render(container);
20039         this.el = this.component.getEl();
20040     },
20041
20042     // private
20043     activate : function(){
20044         if(this.disabled){
20045             return false;
20046         }
20047         this.component.focus();
20048         this.fireEvent("activate", this);
20049         return true;
20050     },
20051
20052     // private
20053     deactivate : function(){
20054         this.fireEvent("deactivate", this);
20055     },
20056
20057     // private
20058     disable : function(){
20059         this.component.disable();
20060         Roo.menu.Adapter.superclass.disable.call(this);
20061     },
20062
20063     // private
20064     enable : function(){
20065         this.component.enable();
20066         Roo.menu.Adapter.superclass.enable.call(this);
20067     }
20068 });/*
20069  * Based on:
20070  * Ext JS Library 1.1.1
20071  * Copyright(c) 2006-2007, Ext JS, LLC.
20072  *
20073  * Originally Released Under LGPL - original licence link has changed is not relivant.
20074  *
20075  * Fork - LGPL
20076  * <script type="text/javascript">
20077  */
20078
20079 /**
20080  * @class Roo.menu.TextItem
20081  * @extends Roo.menu.BaseItem
20082  * Adds a static text string to a menu, usually used as either a heading or group separator.
20083  * Note: old style constructor with text is still supported.
20084  * 
20085  * @constructor
20086  * Creates a new TextItem
20087  * @param {Object} cfg Configuration
20088  */
20089 Roo.menu.TextItem = function(cfg){
20090     if (typeof(cfg) == 'string') {
20091         this.text = cfg;
20092     } else {
20093         Roo.apply(this,cfg);
20094     }
20095     
20096     Roo.menu.TextItem.superclass.constructor.call(this);
20097 };
20098
20099 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20100     /**
20101      * @cfg {Boolean} text Text to show on item.
20102      */
20103     text : '',
20104     
20105     /**
20106      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20107      */
20108     hideOnClick : false,
20109     /**
20110      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20111      */
20112     itemCls : "x-menu-text",
20113
20114     // private
20115     onRender : function(){
20116         var s = document.createElement("span");
20117         s.className = this.itemCls;
20118         s.innerHTML = this.text;
20119         this.el = s;
20120         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20121     }
20122 });/*
20123  * Based on:
20124  * Ext JS Library 1.1.1
20125  * Copyright(c) 2006-2007, Ext JS, LLC.
20126  *
20127  * Originally Released Under LGPL - original licence link has changed is not relivant.
20128  *
20129  * Fork - LGPL
20130  * <script type="text/javascript">
20131  */
20132
20133 /**
20134  * @class Roo.menu.Separator
20135  * @extends Roo.menu.BaseItem
20136  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20137  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20138  * @constructor
20139  * @param {Object} config Configuration options
20140  */
20141 Roo.menu.Separator = function(config){
20142     Roo.menu.Separator.superclass.constructor.call(this, config);
20143 };
20144
20145 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20146     /**
20147      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20148      */
20149     itemCls : "x-menu-sep",
20150     /**
20151      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20152      */
20153     hideOnClick : false,
20154
20155     // private
20156     onRender : function(li){
20157         var s = document.createElement("span");
20158         s.className = this.itemCls;
20159         s.innerHTML = "&#160;";
20160         this.el = s;
20161         li.addClass("x-menu-sep-li");
20162         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20163     }
20164 });/*
20165  * Based on:
20166  * Ext JS Library 1.1.1
20167  * Copyright(c) 2006-2007, Ext JS, LLC.
20168  *
20169  * Originally Released Under LGPL - original licence link has changed is not relivant.
20170  *
20171  * Fork - LGPL
20172  * <script type="text/javascript">
20173  */
20174 /**
20175  * @class Roo.menu.Item
20176  * @extends Roo.menu.BaseItem
20177  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20178  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20179  * activation and click handling.
20180  * @constructor
20181  * Creates a new Item
20182  * @param {Object} config Configuration options
20183  */
20184 Roo.menu.Item = function(config){
20185     Roo.menu.Item.superclass.constructor.call(this, config);
20186     if(this.menu){
20187         this.menu = Roo.menu.MenuMgr.get(this.menu);
20188     }
20189 };
20190 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20191     
20192     /**
20193      * @cfg {String} text
20194      * The text to show on the menu item.
20195      */
20196     text: '',
20197      /**
20198      * @cfg {String} HTML to render in menu
20199      * The text to show on the menu item (HTML version).
20200      */
20201     html: '',
20202     /**
20203      * @cfg {String} icon
20204      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20205      */
20206     icon: undefined,
20207     /**
20208      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20209      */
20210     itemCls : "x-menu-item",
20211     /**
20212      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20213      */
20214     canActivate : true,
20215     /**
20216      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20217      */
20218     showDelay: 200,
20219     // doc'd in BaseItem
20220     hideDelay: 200,
20221
20222     // private
20223     ctype: "Roo.menu.Item",
20224     
20225     // private
20226     onRender : function(container, position){
20227         var el = document.createElement("a");
20228         el.hideFocus = true;
20229         el.unselectable = "on";
20230         el.href = this.href || "#";
20231         if(this.hrefTarget){
20232             el.target = this.hrefTarget;
20233         }
20234         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20235         
20236         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20237         
20238         el.innerHTML = String.format(
20239                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20240                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20241         this.el = el;
20242         Roo.menu.Item.superclass.onRender.call(this, container, position);
20243     },
20244
20245     /**
20246      * Sets the text to display in this menu item
20247      * @param {String} text The text to display
20248      * @param {Boolean} isHTML true to indicate text is pure html.
20249      */
20250     setText : function(text, isHTML){
20251         if (isHTML) {
20252             this.html = text;
20253         } else {
20254             this.text = text;
20255             this.html = '';
20256         }
20257         if(this.rendered){
20258             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20259      
20260             this.el.update(String.format(
20261                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20262                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20263             this.parentMenu.autoWidth();
20264         }
20265     },
20266
20267     // private
20268     handleClick : function(e){
20269         if(!this.href){ // if no link defined, stop the event automatically
20270             e.stopEvent();
20271         }
20272         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20273     },
20274
20275     // private
20276     activate : function(autoExpand){
20277         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20278             this.focus();
20279             if(autoExpand){
20280                 this.expandMenu();
20281             }
20282         }
20283         return true;
20284     },
20285
20286     // private
20287     shouldDeactivate : function(e){
20288         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20289             if(this.menu && this.menu.isVisible()){
20290                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20291             }
20292             return true;
20293         }
20294         return false;
20295     },
20296
20297     // private
20298     deactivate : function(){
20299         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20300         this.hideMenu();
20301     },
20302
20303     // private
20304     expandMenu : function(autoActivate){
20305         if(!this.disabled && this.menu){
20306             clearTimeout(this.hideTimer);
20307             delete this.hideTimer;
20308             if(!this.menu.isVisible() && !this.showTimer){
20309                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20310             }else if (this.menu.isVisible() && autoActivate){
20311                 this.menu.tryActivate(0, 1);
20312             }
20313         }
20314     },
20315
20316     // private
20317     deferExpand : function(autoActivate){
20318         delete this.showTimer;
20319         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20320         if(autoActivate){
20321             this.menu.tryActivate(0, 1);
20322         }
20323     },
20324
20325     // private
20326     hideMenu : function(){
20327         clearTimeout(this.showTimer);
20328         delete this.showTimer;
20329         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20330             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20331         }
20332     },
20333
20334     // private
20335     deferHide : function(){
20336         delete this.hideTimer;
20337         this.menu.hide();
20338     }
20339 });/*
20340  * Based on:
20341  * Ext JS Library 1.1.1
20342  * Copyright(c) 2006-2007, Ext JS, LLC.
20343  *
20344  * Originally Released Under LGPL - original licence link has changed is not relivant.
20345  *
20346  * Fork - LGPL
20347  * <script type="text/javascript">
20348  */
20349  
20350 /**
20351  * @class Roo.menu.CheckItem
20352  * @extends Roo.menu.Item
20353  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20354  * @constructor
20355  * Creates a new CheckItem
20356  * @param {Object} config Configuration options
20357  */
20358 Roo.menu.CheckItem = function(config){
20359     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20360     this.addEvents({
20361         /**
20362          * @event beforecheckchange
20363          * Fires before the checked value is set, providing an opportunity to cancel if needed
20364          * @param {Roo.menu.CheckItem} this
20365          * @param {Boolean} checked The new checked value that will be set
20366          */
20367         "beforecheckchange" : true,
20368         /**
20369          * @event checkchange
20370          * Fires after the checked value has been set
20371          * @param {Roo.menu.CheckItem} this
20372          * @param {Boolean} checked The checked value that was set
20373          */
20374         "checkchange" : true
20375     });
20376     if(this.checkHandler){
20377         this.on('checkchange', this.checkHandler, this.scope);
20378     }
20379 };
20380 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20381     /**
20382      * @cfg {String} group
20383      * All check items with the same group name will automatically be grouped into a single-select
20384      * radio button group (defaults to '')
20385      */
20386     /**
20387      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20388      */
20389     itemCls : "x-menu-item x-menu-check-item",
20390     /**
20391      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20392      */
20393     groupClass : "x-menu-group-item",
20394
20395     /**
20396      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20397      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20398      * initialized with checked = true will be rendered as checked.
20399      */
20400     checked: false,
20401
20402     // private
20403     ctype: "Roo.menu.CheckItem",
20404
20405     // private
20406     onRender : function(c){
20407         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20408         if(this.group){
20409             this.el.addClass(this.groupClass);
20410         }
20411         Roo.menu.MenuMgr.registerCheckable(this);
20412         if(this.checked){
20413             this.checked = false;
20414             this.setChecked(true, true);
20415         }
20416     },
20417
20418     // private
20419     destroy : function(){
20420         if(this.rendered){
20421             Roo.menu.MenuMgr.unregisterCheckable(this);
20422         }
20423         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20424     },
20425
20426     /**
20427      * Set the checked state of this item
20428      * @param {Boolean} checked The new checked value
20429      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20430      */
20431     setChecked : function(state, suppressEvent){
20432         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20433             if(this.container){
20434                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20435             }
20436             this.checked = state;
20437             if(suppressEvent !== true){
20438                 this.fireEvent("checkchange", this, state);
20439             }
20440         }
20441     },
20442
20443     // private
20444     handleClick : function(e){
20445        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20446            this.setChecked(!this.checked);
20447        }
20448        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20449     }
20450 });/*
20451  * Based on:
20452  * Ext JS Library 1.1.1
20453  * Copyright(c) 2006-2007, Ext JS, LLC.
20454  *
20455  * Originally Released Under LGPL - original licence link has changed is not relivant.
20456  *
20457  * Fork - LGPL
20458  * <script type="text/javascript">
20459  */
20460  
20461 /**
20462  * @class Roo.menu.DateItem
20463  * @extends Roo.menu.Adapter
20464  * A menu item that wraps the {@link Roo.DatPicker} component.
20465  * @constructor
20466  * Creates a new DateItem
20467  * @param {Object} config Configuration options
20468  */
20469 Roo.menu.DateItem = function(config){
20470     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20471     /** The Roo.DatePicker object @type Roo.DatePicker */
20472     this.picker = this.component;
20473     this.addEvents({select: true});
20474     
20475     this.picker.on("render", function(picker){
20476         picker.getEl().swallowEvent("click");
20477         picker.container.addClass("x-menu-date-item");
20478     });
20479
20480     this.picker.on("select", this.onSelect, this);
20481 };
20482
20483 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20484     // private
20485     onSelect : function(picker, date){
20486         this.fireEvent("select", this, date, picker);
20487         Roo.menu.DateItem.superclass.handleClick.call(this);
20488     }
20489 });/*
20490  * Based on:
20491  * Ext JS Library 1.1.1
20492  * Copyright(c) 2006-2007, Ext JS, LLC.
20493  *
20494  * Originally Released Under LGPL - original licence link has changed is not relivant.
20495  *
20496  * Fork - LGPL
20497  * <script type="text/javascript">
20498  */
20499  
20500 /**
20501  * @class Roo.menu.ColorItem
20502  * @extends Roo.menu.Adapter
20503  * A menu item that wraps the {@link Roo.ColorPalette} component.
20504  * @constructor
20505  * Creates a new ColorItem
20506  * @param {Object} config Configuration options
20507  */
20508 Roo.menu.ColorItem = function(config){
20509     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20510     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20511     this.palette = this.component;
20512     this.relayEvents(this.palette, ["select"]);
20513     if(this.selectHandler){
20514         this.on('select', this.selectHandler, this.scope);
20515     }
20516 };
20517 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20518  * Based on:
20519  * Ext JS Library 1.1.1
20520  * Copyright(c) 2006-2007, Ext JS, LLC.
20521  *
20522  * Originally Released Under LGPL - original licence link has changed is not relivant.
20523  *
20524  * Fork - LGPL
20525  * <script type="text/javascript">
20526  */
20527  
20528
20529 /**
20530  * @class Roo.menu.DateMenu
20531  * @extends Roo.menu.Menu
20532  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20533  * @constructor
20534  * Creates a new DateMenu
20535  * @param {Object} config Configuration options
20536  */
20537 Roo.menu.DateMenu = function(config){
20538     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20539     this.plain = true;
20540     var di = new Roo.menu.DateItem(config);
20541     this.add(di);
20542     /**
20543      * The {@link Roo.DatePicker} instance for this DateMenu
20544      * @type DatePicker
20545      */
20546     this.picker = di.picker;
20547     /**
20548      * @event select
20549      * @param {DatePicker} picker
20550      * @param {Date} date
20551      */
20552     this.relayEvents(di, ["select"]);
20553     this.on('beforeshow', function(){
20554         if(this.picker){
20555             this.picker.hideMonthPicker(false);
20556         }
20557     }, this);
20558 };
20559 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20560     cls:'x-date-menu'
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571  
20572
20573 /**
20574  * @class Roo.menu.ColorMenu
20575  * @extends Roo.menu.Menu
20576  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20577  * @constructor
20578  * Creates a new ColorMenu
20579  * @param {Object} config Configuration options
20580  */
20581 Roo.menu.ColorMenu = function(config){
20582     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20583     this.plain = true;
20584     var ci = new Roo.menu.ColorItem(config);
20585     this.add(ci);
20586     /**
20587      * The {@link Roo.ColorPalette} instance for this ColorMenu
20588      * @type ColorPalette
20589      */
20590     this.palette = ci.palette;
20591     /**
20592      * @event select
20593      * @param {ColorPalette} palette
20594      * @param {String} color
20595      */
20596     this.relayEvents(ci, ["select"]);
20597 };
20598 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20599  * Based on:
20600  * Ext JS Library 1.1.1
20601  * Copyright(c) 2006-2007, Ext JS, LLC.
20602  *
20603  * Originally Released Under LGPL - original licence link has changed is not relivant.
20604  *
20605  * Fork - LGPL
20606  * <script type="text/javascript">
20607  */
20608  
20609 /**
20610  * @class Roo.form.Field
20611  * @extends Roo.BoxComponent
20612  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20613  * @constructor
20614  * Creates a new Field
20615  * @param {Object} config Configuration options
20616  */
20617 Roo.form.Field = function(config){
20618     Roo.form.Field.superclass.constructor.call(this, config);
20619 };
20620
20621 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20622     /**
20623      * @cfg {String} fieldLabel Label to use when rendering a form.
20624      */
20625        /**
20626      * @cfg {String} qtip Mouse over tip
20627      */
20628      
20629     /**
20630      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20631      */
20632     invalidClass : "x-form-invalid",
20633     /**
20634      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20635      */
20636     invalidText : "The value in this field is invalid",
20637     /**
20638      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20639      */
20640     focusClass : "x-form-focus",
20641     /**
20642      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20643       automatic validation (defaults to "keyup").
20644      */
20645     validationEvent : "keyup",
20646     /**
20647      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20648      */
20649     validateOnBlur : true,
20650     /**
20651      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20652      */
20653     validationDelay : 250,
20654     /**
20655      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20656      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20657      */
20658     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20659     /**
20660      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20661      */
20662     fieldClass : "x-form-field",
20663     /**
20664      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20665      *<pre>
20666 Value         Description
20667 -----------   ----------------------------------------------------------------------
20668 qtip          Display a quick tip when the user hovers over the field
20669 title         Display a default browser title attribute popup
20670 under         Add a block div beneath the field containing the error text
20671 side          Add an error icon to the right of the field with a popup on hover
20672 [element id]  Add the error text directly to the innerHTML of the specified element
20673 </pre>
20674      */
20675     msgTarget : 'qtip',
20676     /**
20677      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20678      */
20679     msgFx : 'normal',
20680
20681     /**
20682      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20683      */
20684     readOnly : false,
20685
20686     /**
20687      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20688      */
20689     disabled : false,
20690
20691     /**
20692      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20693      */
20694     inputType : undefined,
20695     
20696     /**
20697      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20698          */
20699         tabIndex : undefined,
20700         
20701     // private
20702     isFormField : true,
20703
20704     // private
20705     hasFocus : false,
20706     /**
20707      * @property {Roo.Element} fieldEl
20708      * Element Containing the rendered Field (with label etc.)
20709      */
20710     /**
20711      * @cfg {Mixed} value A value to initialize this field with.
20712      */
20713     value : undefined,
20714
20715     /**
20716      * @cfg {String} name The field's HTML name attribute.
20717      */
20718     /**
20719      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20720      */
20721     // private
20722     loadedValue : false,
20723      
20724      
20725         // private ??
20726         initComponent : function(){
20727         Roo.form.Field.superclass.initComponent.call(this);
20728         this.addEvents({
20729             /**
20730              * @event focus
20731              * Fires when this field receives input focus.
20732              * @param {Roo.form.Field} this
20733              */
20734             focus : true,
20735             /**
20736              * @event blur
20737              * Fires when this field loses input focus.
20738              * @param {Roo.form.Field} this
20739              */
20740             blur : true,
20741             /**
20742              * @event specialkey
20743              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20744              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20745              * @param {Roo.form.Field} this
20746              * @param {Roo.EventObject} e The event object
20747              */
20748             specialkey : true,
20749             /**
20750              * @event change
20751              * Fires just before the field blurs if the field value has changed.
20752              * @param {Roo.form.Field} this
20753              * @param {Mixed} newValue The new value
20754              * @param {Mixed} oldValue The original value
20755              */
20756             change : true,
20757             /**
20758              * @event invalid
20759              * Fires after the field has been marked as invalid.
20760              * @param {Roo.form.Field} this
20761              * @param {String} msg The validation message
20762              */
20763             invalid : true,
20764             /**
20765              * @event valid
20766              * Fires after the field has been validated with no errors.
20767              * @param {Roo.form.Field} this
20768              */
20769             valid : true,
20770              /**
20771              * @event keyup
20772              * Fires after the key up
20773              * @param {Roo.form.Field} this
20774              * @param {Roo.EventObject}  e The event Object
20775              */
20776             keyup : true
20777         });
20778     },
20779
20780     /**
20781      * Returns the name attribute of the field if available
20782      * @return {String} name The field name
20783      */
20784     getName: function(){
20785          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20786     },
20787
20788     // private
20789     onRender : function(ct, position){
20790         Roo.form.Field.superclass.onRender.call(this, ct, position);
20791         if(!this.el){
20792             var cfg = this.getAutoCreate();
20793             if(!cfg.name){
20794                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20795             }
20796             if (!cfg.name.length) {
20797                 delete cfg.name;
20798             }
20799             if(this.inputType){
20800                 cfg.type = this.inputType;
20801             }
20802             this.el = ct.createChild(cfg, position);
20803         }
20804         var type = this.el.dom.type;
20805         if(type){
20806             if(type == 'password'){
20807                 type = 'text';
20808             }
20809             this.el.addClass('x-form-'+type);
20810         }
20811         if(this.readOnly){
20812             this.el.dom.readOnly = true;
20813         }
20814         if(this.tabIndex !== undefined){
20815             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20816         }
20817
20818         this.el.addClass([this.fieldClass, this.cls]);
20819         this.initValue();
20820     },
20821
20822     /**
20823      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20824      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20825      * @return {Roo.form.Field} this
20826      */
20827     applyTo : function(target){
20828         this.allowDomMove = false;
20829         this.el = Roo.get(target);
20830         this.render(this.el.dom.parentNode);
20831         return this;
20832     },
20833
20834     // private
20835     initValue : function(){
20836         if(this.value !== undefined){
20837             this.setValue(this.value);
20838         }else if(this.el.dom.value.length > 0){
20839             this.setValue(this.el.dom.value);
20840         }
20841     },
20842
20843     /**
20844      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20845      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
20846      */
20847     isDirty : function() {
20848         if(this.disabled) {
20849             return false;
20850         }
20851         return String(this.getValue()) !== String(this.originalValue);
20852     },
20853
20854     /**
20855      * stores the current value in loadedValue
20856      */
20857     resetHasChanged : function()
20858     {
20859         this.loadedValue = String(this.getValue());
20860     },
20861     /**
20862      * checks the current value against the 'loaded' value.
20863      * Note - will return false if 'resetHasChanged' has not been called first.
20864      */
20865     hasChanged : function()
20866     {
20867         if(this.disabled || this.readOnly) {
20868             return false;
20869         }
20870         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
20871     },
20872     
20873     
20874     
20875     // private
20876     afterRender : function(){
20877         Roo.form.Field.superclass.afterRender.call(this);
20878         this.initEvents();
20879     },
20880
20881     // private
20882     fireKey : function(e){
20883         //Roo.log('field ' + e.getKey());
20884         if(e.isNavKeyPress()){
20885             this.fireEvent("specialkey", this, e);
20886         }
20887     },
20888
20889     /**
20890      * Resets the current field value to the originally loaded value and clears any validation messages
20891      */
20892     reset : function(){
20893         this.setValue(this.resetValue);
20894         this.clearInvalid();
20895     },
20896
20897     // private
20898     initEvents : function(){
20899         // safari killled keypress - so keydown is now used..
20900         this.el.on("keydown" , this.fireKey,  this);
20901         this.el.on("focus", this.onFocus,  this);
20902         this.el.on("blur", this.onBlur,  this);
20903         this.el.relayEvent('keyup', this);
20904
20905         // reference to original value for reset
20906         this.originalValue = this.getValue();
20907         this.resetValue =  this.getValue();
20908     },
20909
20910     // private
20911     onFocus : function(){
20912         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20913             this.el.addClass(this.focusClass);
20914         }
20915         if(!this.hasFocus){
20916             this.hasFocus = true;
20917             this.startValue = this.getValue();
20918             this.fireEvent("focus", this);
20919         }
20920     },
20921
20922     beforeBlur : Roo.emptyFn,
20923
20924     // private
20925     onBlur : function(){
20926         this.beforeBlur();
20927         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20928             this.el.removeClass(this.focusClass);
20929         }
20930         this.hasFocus = false;
20931         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20932             this.validate();
20933         }
20934         var v = this.getValue();
20935         if(String(v) !== String(this.startValue)){
20936             this.fireEvent('change', this, v, this.startValue);
20937         }
20938         this.fireEvent("blur", this);
20939     },
20940
20941     /**
20942      * Returns whether or not the field value is currently valid
20943      * @param {Boolean} preventMark True to disable marking the field invalid
20944      * @return {Boolean} True if the value is valid, else false
20945      */
20946     isValid : function(preventMark){
20947         if(this.disabled){
20948             return true;
20949         }
20950         var restore = this.preventMark;
20951         this.preventMark = preventMark === true;
20952         var v = this.validateValue(this.processValue(this.getRawValue()));
20953         this.preventMark = restore;
20954         return v;
20955     },
20956
20957     /**
20958      * Validates the field value
20959      * @return {Boolean} True if the value is valid, else false
20960      */
20961     validate : function(){
20962         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20963             this.clearInvalid();
20964             return true;
20965         }
20966         return false;
20967     },
20968
20969     processValue : function(value){
20970         return value;
20971     },
20972
20973     // private
20974     // Subclasses should provide the validation implementation by overriding this
20975     validateValue : function(value){
20976         return true;
20977     },
20978
20979     /**
20980      * Mark this field as invalid
20981      * @param {String} msg The validation message
20982      */
20983     markInvalid : function(msg){
20984         if(!this.rendered || this.preventMark){ // not rendered
20985             return;
20986         }
20987         
20988         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20989         
20990         obj.el.addClass(this.invalidClass);
20991         msg = msg || this.invalidText;
20992         switch(this.msgTarget){
20993             case 'qtip':
20994                 obj.el.dom.qtip = msg;
20995                 obj.el.dom.qclass = 'x-form-invalid-tip';
20996                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20997                     Roo.QuickTips.enable();
20998                 }
20999                 break;
21000             case 'title':
21001                 this.el.dom.title = msg;
21002                 break;
21003             case 'under':
21004                 if(!this.errorEl){
21005                     var elp = this.el.findParent('.x-form-element', 5, true);
21006                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21007                     this.errorEl.setWidth(elp.getWidth(true)-20);
21008                 }
21009                 this.errorEl.update(msg);
21010                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21011                 break;
21012             case 'side':
21013                 if(!this.errorIcon){
21014                     var elp = this.el.findParent('.x-form-element', 5, true);
21015                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21016                 }
21017                 this.alignErrorIcon();
21018                 this.errorIcon.dom.qtip = msg;
21019                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21020                 this.errorIcon.show();
21021                 this.on('resize', this.alignErrorIcon, this);
21022                 break;
21023             default:
21024                 var t = Roo.getDom(this.msgTarget);
21025                 t.innerHTML = msg;
21026                 t.style.display = this.msgDisplay;
21027                 break;
21028         }
21029         this.fireEvent('invalid', this, msg);
21030     },
21031
21032     // private
21033     alignErrorIcon : function(){
21034         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21035     },
21036
21037     /**
21038      * Clear any invalid styles/messages for this field
21039      */
21040     clearInvalid : function(){
21041         if(!this.rendered || this.preventMark){ // not rendered
21042             return;
21043         }
21044         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21045         
21046         obj.el.removeClass(this.invalidClass);
21047         switch(this.msgTarget){
21048             case 'qtip':
21049                 obj.el.dom.qtip = '';
21050                 break;
21051             case 'title':
21052                 this.el.dom.title = '';
21053                 break;
21054             case 'under':
21055                 if(this.errorEl){
21056                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21057                 }
21058                 break;
21059             case 'side':
21060                 if(this.errorIcon){
21061                     this.errorIcon.dom.qtip = '';
21062                     this.errorIcon.hide();
21063                     this.un('resize', this.alignErrorIcon, this);
21064                 }
21065                 break;
21066             default:
21067                 var t = Roo.getDom(this.msgTarget);
21068                 t.innerHTML = '';
21069                 t.style.display = 'none';
21070                 break;
21071         }
21072         this.fireEvent('valid', this);
21073     },
21074
21075     /**
21076      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21077      * @return {Mixed} value The field value
21078      */
21079     getRawValue : function(){
21080         var v = this.el.getValue();
21081         
21082         return v;
21083     },
21084
21085     /**
21086      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21087      * @return {Mixed} value The field value
21088      */
21089     getValue : function(){
21090         var v = this.el.getValue();
21091          
21092         return v;
21093     },
21094
21095     /**
21096      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21097      * @param {Mixed} value The value to set
21098      */
21099     setRawValue : function(v){
21100         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21101     },
21102
21103     /**
21104      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21105      * @param {Mixed} value The value to set
21106      */
21107     setValue : function(v){
21108         this.value = v;
21109         if(this.rendered){
21110             this.el.dom.value = (v === null || v === undefined ? '' : v);
21111              this.validate();
21112         }
21113     },
21114
21115     adjustSize : function(w, h){
21116         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21117         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21118         return s;
21119     },
21120
21121     adjustWidth : function(tag, w){
21122         tag = tag.toLowerCase();
21123         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21124             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21125                 if(tag == 'input'){
21126                     return w + 2;
21127                 }
21128                 if(tag == 'textarea'){
21129                     return w-2;
21130                 }
21131             }else if(Roo.isOpera){
21132                 if(tag == 'input'){
21133                     return w + 2;
21134                 }
21135                 if(tag == 'textarea'){
21136                     return w-2;
21137                 }
21138             }
21139         }
21140         return w;
21141     }
21142 });
21143
21144
21145 // anything other than normal should be considered experimental
21146 Roo.form.Field.msgFx = {
21147     normal : {
21148         show: function(msgEl, f){
21149             msgEl.setDisplayed('block');
21150         },
21151
21152         hide : function(msgEl, f){
21153             msgEl.setDisplayed(false).update('');
21154         }
21155     },
21156
21157     slide : {
21158         show: function(msgEl, f){
21159             msgEl.slideIn('t', {stopFx:true});
21160         },
21161
21162         hide : function(msgEl, f){
21163             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21164         }
21165     },
21166
21167     slideRight : {
21168         show: function(msgEl, f){
21169             msgEl.fixDisplay();
21170             msgEl.alignTo(f.el, 'tl-tr');
21171             msgEl.slideIn('l', {stopFx:true});
21172         },
21173
21174         hide : function(msgEl, f){
21175             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21176         }
21177     }
21178 };/*
21179  * Based on:
21180  * Ext JS Library 1.1.1
21181  * Copyright(c) 2006-2007, Ext JS, LLC.
21182  *
21183  * Originally Released Under LGPL - original licence link has changed is not relivant.
21184  *
21185  * Fork - LGPL
21186  * <script type="text/javascript">
21187  */
21188  
21189
21190 /**
21191  * @class Roo.form.TextField
21192  * @extends Roo.form.Field
21193  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21194  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21195  * @constructor
21196  * Creates a new TextField
21197  * @param {Object} config Configuration options
21198  */
21199 Roo.form.TextField = function(config){
21200     Roo.form.TextField.superclass.constructor.call(this, config);
21201     this.addEvents({
21202         /**
21203          * @event autosize
21204          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21205          * according to the default logic, but this event provides a hook for the developer to apply additional
21206          * logic at runtime to resize the field if needed.
21207              * @param {Roo.form.Field} this This text field
21208              * @param {Number} width The new field width
21209              */
21210         autosize : true
21211     });
21212 };
21213
21214 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21215     /**
21216      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21217      */
21218     grow : false,
21219     /**
21220      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21221      */
21222     growMin : 30,
21223     /**
21224      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21225      */
21226     growMax : 800,
21227     /**
21228      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21229      */
21230     vtype : null,
21231     /**
21232      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21233      */
21234     maskRe : null,
21235     /**
21236      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21237      */
21238     disableKeyFilter : false,
21239     /**
21240      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21241      */
21242     allowBlank : true,
21243     /**
21244      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21245      */
21246     minLength : 0,
21247     /**
21248      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21249      */
21250     maxLength : Number.MAX_VALUE,
21251     /**
21252      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21253      */
21254     minLengthText : "The minimum length for this field is {0}",
21255     /**
21256      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21257      */
21258     maxLengthText : "The maximum length for this field is {0}",
21259     /**
21260      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21261      */
21262     selectOnFocus : false,
21263     /**
21264      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21265      */
21266     blankText : "This field is required",
21267     /**
21268      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21269      * If available, this function will be called only after the basic validators all return true, and will be passed the
21270      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21271      */
21272     validator : null,
21273     /**
21274      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21275      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21276      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21277      */
21278     regex : null,
21279     /**
21280      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21281      */
21282     regexText : "",
21283     /**
21284      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21285      */
21286     emptyText : null,
21287    
21288
21289     // private
21290     initEvents : function()
21291     {
21292         if (this.emptyText) {
21293             this.el.attr('placeholder', this.emptyText);
21294         }
21295         
21296         Roo.form.TextField.superclass.initEvents.call(this);
21297         if(this.validationEvent == 'keyup'){
21298             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21299             this.el.on('keyup', this.filterValidation, this);
21300         }
21301         else if(this.validationEvent !== false){
21302             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21303         }
21304         
21305         if(this.selectOnFocus){
21306             this.on("focus", this.preFocus, this);
21307             
21308         }
21309         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21310             this.el.on("keypress", this.filterKeys, this);
21311         }
21312         if(this.grow){
21313             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21314             this.el.on("click", this.autoSize,  this);
21315         }
21316         if(this.el.is('input[type=password]') && Roo.isSafari){
21317             this.el.on('keydown', this.SafariOnKeyDown, this);
21318         }
21319     },
21320
21321     processValue : function(value){
21322         if(this.stripCharsRe){
21323             var newValue = value.replace(this.stripCharsRe, '');
21324             if(newValue !== value){
21325                 this.setRawValue(newValue);
21326                 return newValue;
21327             }
21328         }
21329         return value;
21330     },
21331
21332     filterValidation : function(e){
21333         if(!e.isNavKeyPress()){
21334             this.validationTask.delay(this.validationDelay);
21335         }
21336     },
21337
21338     // private
21339     onKeyUp : function(e){
21340         if(!e.isNavKeyPress()){
21341             this.autoSize();
21342         }
21343     },
21344
21345     /**
21346      * Resets the current field value to the originally-loaded value and clears any validation messages.
21347      *  
21348      */
21349     reset : function(){
21350         Roo.form.TextField.superclass.reset.call(this);
21351        
21352     },
21353
21354     
21355     // private
21356     preFocus : function(){
21357         
21358         if(this.selectOnFocus){
21359             this.el.dom.select();
21360         }
21361     },
21362
21363     
21364     // private
21365     filterKeys : function(e){
21366         var k = e.getKey();
21367         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21368             return;
21369         }
21370         var c = e.getCharCode(), cc = String.fromCharCode(c);
21371         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21372             return;
21373         }
21374         if(!this.maskRe.test(cc)){
21375             e.stopEvent();
21376         }
21377     },
21378
21379     setValue : function(v){
21380         
21381         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21382         
21383         this.autoSize();
21384     },
21385
21386     /**
21387      * Validates a value according to the field's validation rules and marks the field as invalid
21388      * if the validation fails
21389      * @param {Mixed} value The value to validate
21390      * @return {Boolean} True if the value is valid, else false
21391      */
21392     validateValue : function(value){
21393         if(value.length < 1)  { // if it's blank
21394              if(this.allowBlank){
21395                 this.clearInvalid();
21396                 return true;
21397              }else{
21398                 this.markInvalid(this.blankText);
21399                 return false;
21400              }
21401         }
21402         if(value.length < this.minLength){
21403             this.markInvalid(String.format(this.minLengthText, this.minLength));
21404             return false;
21405         }
21406         if(value.length > this.maxLength){
21407             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21408             return false;
21409         }
21410         if(this.vtype){
21411             var vt = Roo.form.VTypes;
21412             if(!vt[this.vtype](value, this)){
21413                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21414                 return false;
21415             }
21416         }
21417         if(typeof this.validator == "function"){
21418             var msg = this.validator(value);
21419             if(msg !== true){
21420                 this.markInvalid(msg);
21421                 return false;
21422             }
21423         }
21424         if(this.regex && !this.regex.test(value)){
21425             this.markInvalid(this.regexText);
21426             return false;
21427         }
21428         return true;
21429     },
21430
21431     /**
21432      * Selects text in this field
21433      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21434      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21435      */
21436     selectText : function(start, end){
21437         var v = this.getRawValue();
21438         if(v.length > 0){
21439             start = start === undefined ? 0 : start;
21440             end = end === undefined ? v.length : end;
21441             var d = this.el.dom;
21442             if(d.setSelectionRange){
21443                 d.setSelectionRange(start, end);
21444             }else if(d.createTextRange){
21445                 var range = d.createTextRange();
21446                 range.moveStart("character", start);
21447                 range.moveEnd("character", v.length-end);
21448                 range.select();
21449             }
21450         }
21451     },
21452
21453     /**
21454      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21455      * This only takes effect if grow = true, and fires the autosize event.
21456      */
21457     autoSize : function(){
21458         if(!this.grow || !this.rendered){
21459             return;
21460         }
21461         if(!this.metrics){
21462             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21463         }
21464         var el = this.el;
21465         var v = el.dom.value;
21466         var d = document.createElement('div');
21467         d.appendChild(document.createTextNode(v));
21468         v = d.innerHTML;
21469         d = null;
21470         v += "&#160;";
21471         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21472         this.el.setWidth(w);
21473         this.fireEvent("autosize", this, w);
21474     },
21475     
21476     // private
21477     SafariOnKeyDown : function(event)
21478     {
21479         // this is a workaround for a password hang bug on chrome/ webkit.
21480         
21481         var isSelectAll = false;
21482         
21483         if(this.el.dom.selectionEnd > 0){
21484             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21485         }
21486         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21487             event.preventDefault();
21488             this.setValue('');
21489             return;
21490         }
21491         
21492         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21493             
21494             event.preventDefault();
21495             // this is very hacky as keydown always get's upper case.
21496             
21497             var cc = String.fromCharCode(event.getCharCode());
21498             
21499             
21500             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21501             
21502         }
21503         
21504         
21505     }
21506 });/*
21507  * Based on:
21508  * Ext JS Library 1.1.1
21509  * Copyright(c) 2006-2007, Ext JS, LLC.
21510  *
21511  * Originally Released Under LGPL - original licence link has changed is not relivant.
21512  *
21513  * Fork - LGPL
21514  * <script type="text/javascript">
21515  */
21516  
21517 /**
21518  * @class Roo.form.Hidden
21519  * @extends Roo.form.TextField
21520  * Simple Hidden element used on forms 
21521  * 
21522  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21523  * 
21524  * @constructor
21525  * Creates a new Hidden form element.
21526  * @param {Object} config Configuration options
21527  */
21528
21529
21530
21531 // easy hidden field...
21532 Roo.form.Hidden = function(config){
21533     Roo.form.Hidden.superclass.constructor.call(this, config);
21534 };
21535   
21536 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21537     fieldLabel:      '',
21538     inputType:      'hidden',
21539     width:          50,
21540     allowBlank:     true,
21541     labelSeparator: '',
21542     hidden:         true,
21543     itemCls :       'x-form-item-display-none'
21544
21545
21546 });
21547
21548
21549 /*
21550  * Based on:
21551  * Ext JS Library 1.1.1
21552  * Copyright(c) 2006-2007, Ext JS, LLC.
21553  *
21554  * Originally Released Under LGPL - original licence link has changed is not relivant.
21555  *
21556  * Fork - LGPL
21557  * <script type="text/javascript">
21558  */
21559  
21560 /**
21561  * @class Roo.form.TriggerField
21562  * @extends Roo.form.TextField
21563  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21564  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21565  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21566  * for which you can provide a custom implementation.  For example:
21567  * <pre><code>
21568 var trigger = new Roo.form.TriggerField();
21569 trigger.onTriggerClick = myTriggerFn;
21570 trigger.applyTo('my-field');
21571 </code></pre>
21572  *
21573  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21574  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21575  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21576  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21577  * @constructor
21578  * Create a new TriggerField.
21579  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21580  * to the base TextField)
21581  */
21582 Roo.form.TriggerField = function(config){
21583     this.mimicing = false;
21584     Roo.form.TriggerField.superclass.constructor.call(this, config);
21585 };
21586
21587 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21588     /**
21589      * @cfg {String} triggerClass A CSS class to apply to the trigger
21590      */
21591     /**
21592      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21593      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21594      */
21595     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21596     /**
21597      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21598      */
21599     hideTrigger:false,
21600
21601     /** @cfg {Boolean} grow @hide */
21602     /** @cfg {Number} growMin @hide */
21603     /** @cfg {Number} growMax @hide */
21604
21605     /**
21606      * @hide 
21607      * @method
21608      */
21609     autoSize: Roo.emptyFn,
21610     // private
21611     monitorTab : true,
21612     // private
21613     deferHeight : true,
21614
21615     
21616     actionMode : 'wrap',
21617     // private
21618     onResize : function(w, h){
21619         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21620         if(typeof w == 'number'){
21621             var x = w - this.trigger.getWidth();
21622             this.el.setWidth(this.adjustWidth('input', x));
21623             this.trigger.setStyle('left', x+'px');
21624         }
21625     },
21626
21627     // private
21628     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21629
21630     // private
21631     getResizeEl : function(){
21632         return this.wrap;
21633     },
21634
21635     // private
21636     getPositionEl : function(){
21637         return this.wrap;
21638     },
21639
21640     // private
21641     alignErrorIcon : function(){
21642         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21643     },
21644
21645     // private
21646     onRender : function(ct, position){
21647         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21648         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21649         this.trigger = this.wrap.createChild(this.triggerConfig ||
21650                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21651         if(this.hideTrigger){
21652             this.trigger.setDisplayed(false);
21653         }
21654         this.initTrigger();
21655         if(!this.width){
21656             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21657         }
21658     },
21659
21660     // private
21661     initTrigger : function(){
21662         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21663         this.trigger.addClassOnOver('x-form-trigger-over');
21664         this.trigger.addClassOnClick('x-form-trigger-click');
21665     },
21666
21667     // private
21668     onDestroy : function(){
21669         if(this.trigger){
21670             this.trigger.removeAllListeners();
21671             this.trigger.remove();
21672         }
21673         if(this.wrap){
21674             this.wrap.remove();
21675         }
21676         Roo.form.TriggerField.superclass.onDestroy.call(this);
21677     },
21678
21679     // private
21680     onFocus : function(){
21681         Roo.form.TriggerField.superclass.onFocus.call(this);
21682         if(!this.mimicing){
21683             this.wrap.addClass('x-trigger-wrap-focus');
21684             this.mimicing = true;
21685             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21686             if(this.monitorTab){
21687                 this.el.on("keydown", this.checkTab, this);
21688             }
21689         }
21690     },
21691
21692     // private
21693     checkTab : function(e){
21694         if(e.getKey() == e.TAB){
21695             this.triggerBlur();
21696         }
21697     },
21698
21699     // private
21700     onBlur : function(){
21701         // do nothing
21702     },
21703
21704     // private
21705     mimicBlur : function(e, t){
21706         if(!this.wrap.contains(t) && this.validateBlur()){
21707             this.triggerBlur();
21708         }
21709     },
21710
21711     // private
21712     triggerBlur : function(){
21713         this.mimicing = false;
21714         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21715         if(this.monitorTab){
21716             this.el.un("keydown", this.checkTab, this);
21717         }
21718         this.wrap.removeClass('x-trigger-wrap-focus');
21719         Roo.form.TriggerField.superclass.onBlur.call(this);
21720     },
21721
21722     // private
21723     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21724     validateBlur : function(e, t){
21725         return true;
21726     },
21727
21728     // private
21729     onDisable : function(){
21730         Roo.form.TriggerField.superclass.onDisable.call(this);
21731         if(this.wrap){
21732             this.wrap.addClass('x-item-disabled');
21733         }
21734     },
21735
21736     // private
21737     onEnable : function(){
21738         Roo.form.TriggerField.superclass.onEnable.call(this);
21739         if(this.wrap){
21740             this.wrap.removeClass('x-item-disabled');
21741         }
21742     },
21743
21744     // private
21745     onShow : function(){
21746         var ae = this.getActionEl();
21747         
21748         if(ae){
21749             ae.dom.style.display = '';
21750             ae.dom.style.visibility = 'visible';
21751         }
21752     },
21753
21754     // private
21755     
21756     onHide : function(){
21757         var ae = this.getActionEl();
21758         ae.dom.style.display = 'none';
21759     },
21760
21761     /**
21762      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21763      * by an implementing function.
21764      * @method
21765      * @param {EventObject} e
21766      */
21767     onTriggerClick : Roo.emptyFn
21768 });
21769
21770 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21771 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21772 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21773 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21774     initComponent : function(){
21775         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21776
21777         this.triggerConfig = {
21778             tag:'span', cls:'x-form-twin-triggers', cn:[
21779             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21780             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21781         ]};
21782     },
21783
21784     getTrigger : function(index){
21785         return this.triggers[index];
21786     },
21787
21788     initTrigger : function(){
21789         var ts = this.trigger.select('.x-form-trigger', true);
21790         this.wrap.setStyle('overflow', 'hidden');
21791         var triggerField = this;
21792         ts.each(function(t, all, index){
21793             t.hide = function(){
21794                 var w = triggerField.wrap.getWidth();
21795                 this.dom.style.display = 'none';
21796                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21797             };
21798             t.show = function(){
21799                 var w = triggerField.wrap.getWidth();
21800                 this.dom.style.display = '';
21801                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21802             };
21803             var triggerIndex = 'Trigger'+(index+1);
21804
21805             if(this['hide'+triggerIndex]){
21806                 t.dom.style.display = 'none';
21807             }
21808             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21809             t.addClassOnOver('x-form-trigger-over');
21810             t.addClassOnClick('x-form-trigger-click');
21811         }, this);
21812         this.triggers = ts.elements;
21813     },
21814
21815     onTrigger1Click : Roo.emptyFn,
21816     onTrigger2Click : Roo.emptyFn
21817 });/*
21818  * Based on:
21819  * Ext JS Library 1.1.1
21820  * Copyright(c) 2006-2007, Ext JS, LLC.
21821  *
21822  * Originally Released Under LGPL - original licence link has changed is not relivant.
21823  *
21824  * Fork - LGPL
21825  * <script type="text/javascript">
21826  */
21827  
21828 /**
21829  * @class Roo.form.TextArea
21830  * @extends Roo.form.TextField
21831  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21832  * support for auto-sizing.
21833  * @constructor
21834  * Creates a new TextArea
21835  * @param {Object} config Configuration options
21836  */
21837 Roo.form.TextArea = function(config){
21838     Roo.form.TextArea.superclass.constructor.call(this, config);
21839     // these are provided exchanges for backwards compat
21840     // minHeight/maxHeight were replaced by growMin/growMax to be
21841     // compatible with TextField growing config values
21842     if(this.minHeight !== undefined){
21843         this.growMin = this.minHeight;
21844     }
21845     if(this.maxHeight !== undefined){
21846         this.growMax = this.maxHeight;
21847     }
21848 };
21849
21850 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21851     /**
21852      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21853      */
21854     growMin : 60,
21855     /**
21856      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21857      */
21858     growMax: 1000,
21859     /**
21860      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21861      * in the field (equivalent to setting overflow: hidden, defaults to false)
21862      */
21863     preventScrollbars: false,
21864     /**
21865      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21866      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21867      */
21868
21869     // private
21870     onRender : function(ct, position){
21871         if(!this.el){
21872             this.defaultAutoCreate = {
21873                 tag: "textarea",
21874                 style:"width:300px;height:60px;",
21875                 autocomplete: "new-password"
21876             };
21877         }
21878         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21879         if(this.grow){
21880             this.textSizeEl = Roo.DomHelper.append(document.body, {
21881                 tag: "pre", cls: "x-form-grow-sizer"
21882             });
21883             if(this.preventScrollbars){
21884                 this.el.setStyle("overflow", "hidden");
21885             }
21886             this.el.setHeight(this.growMin);
21887         }
21888     },
21889
21890     onDestroy : function(){
21891         if(this.textSizeEl){
21892             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21893         }
21894         Roo.form.TextArea.superclass.onDestroy.call(this);
21895     },
21896
21897     // private
21898     onKeyUp : function(e){
21899         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21900             this.autoSize();
21901         }
21902     },
21903
21904     /**
21905      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21906      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21907      */
21908     autoSize : function(){
21909         if(!this.grow || !this.textSizeEl){
21910             return;
21911         }
21912         var el = this.el;
21913         var v = el.dom.value;
21914         var ts = this.textSizeEl;
21915
21916         ts.innerHTML = '';
21917         ts.appendChild(document.createTextNode(v));
21918         v = ts.innerHTML;
21919
21920         Roo.fly(ts).setWidth(this.el.getWidth());
21921         if(v.length < 1){
21922             v = "&#160;&#160;";
21923         }else{
21924             if(Roo.isIE){
21925                 v = v.replace(/\n/g, '<p>&#160;</p>');
21926             }
21927             v += "&#160;\n&#160;";
21928         }
21929         ts.innerHTML = v;
21930         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21931         if(h != this.lastHeight){
21932             this.lastHeight = h;
21933             this.el.setHeight(h);
21934             this.fireEvent("autosize", this, h);
21935         }
21936     }
21937 });/*
21938  * Based on:
21939  * Ext JS Library 1.1.1
21940  * Copyright(c) 2006-2007, Ext JS, LLC.
21941  *
21942  * Originally Released Under LGPL - original licence link has changed is not relivant.
21943  *
21944  * Fork - LGPL
21945  * <script type="text/javascript">
21946  */
21947  
21948
21949 /**
21950  * @class Roo.form.NumberField
21951  * @extends Roo.form.TextField
21952  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21953  * @constructor
21954  * Creates a new NumberField
21955  * @param {Object} config Configuration options
21956  */
21957 Roo.form.NumberField = function(config){
21958     Roo.form.NumberField.superclass.constructor.call(this, config);
21959 };
21960
21961 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21962     /**
21963      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21964      */
21965     fieldClass: "x-form-field x-form-num-field",
21966     /**
21967      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21968      */
21969     allowDecimals : true,
21970     /**
21971      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21972      */
21973     decimalSeparator : ".",
21974     /**
21975      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21976      */
21977     decimalPrecision : 2,
21978     /**
21979      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21980      */
21981     allowNegative : true,
21982     /**
21983      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21984      */
21985     minValue : Number.NEGATIVE_INFINITY,
21986     /**
21987      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21988      */
21989     maxValue : Number.MAX_VALUE,
21990     /**
21991      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21992      */
21993     minText : "The minimum value for this field is {0}",
21994     /**
21995      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21996      */
21997     maxText : "The maximum value for this field is {0}",
21998     /**
21999      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22000      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22001      */
22002     nanText : "{0} is not a valid number",
22003
22004     // private
22005     initEvents : function(){
22006         Roo.form.NumberField.superclass.initEvents.call(this);
22007         var allowed = "0123456789";
22008         if(this.allowDecimals){
22009             allowed += this.decimalSeparator;
22010         }
22011         if(this.allowNegative){
22012             allowed += "-";
22013         }
22014         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22015         var keyPress = function(e){
22016             var k = e.getKey();
22017             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22018                 return;
22019             }
22020             var c = e.getCharCode();
22021             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22022                 e.stopEvent();
22023             }
22024         };
22025         this.el.on("keypress", keyPress, this);
22026     },
22027
22028     // private
22029     validateValue : function(value){
22030         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22031             return false;
22032         }
22033         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22034              return true;
22035         }
22036         var num = this.parseValue(value);
22037         if(isNaN(num)){
22038             this.markInvalid(String.format(this.nanText, value));
22039             return false;
22040         }
22041         if(num < this.minValue){
22042             this.markInvalid(String.format(this.minText, this.minValue));
22043             return false;
22044         }
22045         if(num > this.maxValue){
22046             this.markInvalid(String.format(this.maxText, this.maxValue));
22047             return false;
22048         }
22049         return true;
22050     },
22051
22052     getValue : function(){
22053         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22054     },
22055
22056     // private
22057     parseValue : function(value){
22058         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22059         return isNaN(value) ? '' : value;
22060     },
22061
22062     // private
22063     fixPrecision : function(value){
22064         var nan = isNaN(value);
22065         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22066             return nan ? '' : value;
22067         }
22068         return parseFloat(value).toFixed(this.decimalPrecision);
22069     },
22070
22071     setValue : function(v){
22072         v = this.fixPrecision(v);
22073         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22074     },
22075
22076     // private
22077     decimalPrecisionFcn : function(v){
22078         return Math.floor(v);
22079     },
22080
22081     beforeBlur : function(){
22082         var v = this.parseValue(this.getRawValue());
22083         if(v){
22084             this.setValue(v);
22085         }
22086     }
22087 });/*
22088  * Based on:
22089  * Ext JS Library 1.1.1
22090  * Copyright(c) 2006-2007, Ext JS, LLC.
22091  *
22092  * Originally Released Under LGPL - original licence link has changed is not relivant.
22093  *
22094  * Fork - LGPL
22095  * <script type="text/javascript">
22096  */
22097  
22098 /**
22099  * @class Roo.form.DateField
22100  * @extends Roo.form.TriggerField
22101  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22102 * @constructor
22103 * Create a new DateField
22104 * @param {Object} config
22105  */
22106 Roo.form.DateField = function(config){
22107     Roo.form.DateField.superclass.constructor.call(this, config);
22108     
22109       this.addEvents({
22110          
22111         /**
22112          * @event select
22113          * Fires when a date is selected
22114              * @param {Roo.form.DateField} combo This combo box
22115              * @param {Date} date The date selected
22116              */
22117         'select' : true
22118          
22119     });
22120     
22121     
22122     if(typeof this.minValue == "string") {
22123         this.minValue = this.parseDate(this.minValue);
22124     }
22125     if(typeof this.maxValue == "string") {
22126         this.maxValue = this.parseDate(this.maxValue);
22127     }
22128     this.ddMatch = null;
22129     if(this.disabledDates){
22130         var dd = this.disabledDates;
22131         var re = "(?:";
22132         for(var i = 0; i < dd.length; i++){
22133             re += dd[i];
22134             if(i != dd.length-1) {
22135                 re += "|";
22136             }
22137         }
22138         this.ddMatch = new RegExp(re + ")");
22139     }
22140 };
22141
22142 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22143     /**
22144      * @cfg {String} format
22145      * The default date format string which can be overriden for localization support.  The format must be
22146      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22147      */
22148     format : "m/d/y",
22149     /**
22150      * @cfg {String} altFormats
22151      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22152      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22153      */
22154     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22155     /**
22156      * @cfg {Array} disabledDays
22157      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22158      */
22159     disabledDays : null,
22160     /**
22161      * @cfg {String} disabledDaysText
22162      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22163      */
22164     disabledDaysText : "Disabled",
22165     /**
22166      * @cfg {Array} disabledDates
22167      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22168      * expression so they are very powerful. Some examples:
22169      * <ul>
22170      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22171      * <li>["03/08", "09/16"] would disable those days for every year</li>
22172      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22173      * <li>["03/../2006"] would disable every day in March 2006</li>
22174      * <li>["^03"] would disable every day in every March</li>
22175      * </ul>
22176      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22177      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22178      */
22179     disabledDates : null,
22180     /**
22181      * @cfg {String} disabledDatesText
22182      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22183      */
22184     disabledDatesText : "Disabled",
22185     /**
22186      * @cfg {Date/String} minValue
22187      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22188      * valid format (defaults to null).
22189      */
22190     minValue : null,
22191     /**
22192      * @cfg {Date/String} maxValue
22193      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22194      * valid format (defaults to null).
22195      */
22196     maxValue : null,
22197     /**
22198      * @cfg {String} minText
22199      * The error text to display when the date in the cell is before minValue (defaults to
22200      * 'The date in this field must be after {minValue}').
22201      */
22202     minText : "The date in this field must be equal to or after {0}",
22203     /**
22204      * @cfg {String} maxText
22205      * The error text to display when the date in the cell is after maxValue (defaults to
22206      * 'The date in this field must be before {maxValue}').
22207      */
22208     maxText : "The date in this field must be equal to or before {0}",
22209     /**
22210      * @cfg {String} invalidText
22211      * The error text to display when the date in the field is invalid (defaults to
22212      * '{value} is not a valid date - it must be in the format {format}').
22213      */
22214     invalidText : "{0} is not a valid date - it must be in the format {1}",
22215     /**
22216      * @cfg {String} triggerClass
22217      * An additional CSS class used to style the trigger button.  The trigger will always get the
22218      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22219      * which displays a calendar icon).
22220      */
22221     triggerClass : 'x-form-date-trigger',
22222     
22223
22224     /**
22225      * @cfg {Boolean} useIso
22226      * if enabled, then the date field will use a hidden field to store the 
22227      * real value as iso formated date. default (false)
22228      */ 
22229     useIso : false,
22230     /**
22231      * @cfg {String/Object} autoCreate
22232      * A DomHelper element spec, or true for a default element spec (defaults to
22233      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22234      */ 
22235     // private
22236     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22237     
22238     // private
22239     hiddenField: false,
22240     
22241     onRender : function(ct, position)
22242     {
22243         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22244         if (this.useIso) {
22245             //this.el.dom.removeAttribute('name'); 
22246             Roo.log("Changing name?");
22247             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22248             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22249                     'before', true);
22250             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22251             // prevent input submission
22252             this.hiddenName = this.name;
22253         }
22254             
22255             
22256     },
22257     
22258     // private
22259     validateValue : function(value)
22260     {
22261         value = this.formatDate(value);
22262         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22263             Roo.log('super failed');
22264             return false;
22265         }
22266         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22267              return true;
22268         }
22269         var svalue = value;
22270         value = this.parseDate(value);
22271         if(!value){
22272             Roo.log('parse date failed' + svalue);
22273             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22274             return false;
22275         }
22276         var time = value.getTime();
22277         if(this.minValue && time < this.minValue.getTime()){
22278             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22279             return false;
22280         }
22281         if(this.maxValue && time > this.maxValue.getTime()){
22282             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22283             return false;
22284         }
22285         if(this.disabledDays){
22286             var day = value.getDay();
22287             for(var i = 0; i < this.disabledDays.length; i++) {
22288                 if(day === this.disabledDays[i]){
22289                     this.markInvalid(this.disabledDaysText);
22290                     return false;
22291                 }
22292             }
22293         }
22294         var fvalue = this.formatDate(value);
22295         if(this.ddMatch && this.ddMatch.test(fvalue)){
22296             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22297             return false;
22298         }
22299         return true;
22300     },
22301
22302     // private
22303     // Provides logic to override the default TriggerField.validateBlur which just returns true
22304     validateBlur : function(){
22305         return !this.menu || !this.menu.isVisible();
22306     },
22307     
22308     getName: function()
22309     {
22310         // returns hidden if it's set..
22311         if (!this.rendered) {return ''};
22312         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22313         
22314     },
22315
22316     /**
22317      * Returns the current date value of the date field.
22318      * @return {Date} The date value
22319      */
22320     getValue : function(){
22321         
22322         return  this.hiddenField ?
22323                 this.hiddenField.value :
22324                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22325     },
22326
22327     /**
22328      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22329      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22330      * (the default format used is "m/d/y").
22331      * <br />Usage:
22332      * <pre><code>
22333 //All of these calls set the same date value (May 4, 2006)
22334
22335 //Pass a date object:
22336 var dt = new Date('5/4/06');
22337 dateField.setValue(dt);
22338
22339 //Pass a date string (default format):
22340 dateField.setValue('5/4/06');
22341
22342 //Pass a date string (custom format):
22343 dateField.format = 'Y-m-d';
22344 dateField.setValue('2006-5-4');
22345 </code></pre>
22346      * @param {String/Date} date The date or valid date string
22347      */
22348     setValue : function(date){
22349         if (this.hiddenField) {
22350             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22351         }
22352         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22353         // make sure the value field is always stored as a date..
22354         this.value = this.parseDate(date);
22355         
22356         
22357     },
22358
22359     // private
22360     parseDate : function(value){
22361         if(!value || value instanceof Date){
22362             return value;
22363         }
22364         var v = Date.parseDate(value, this.format);
22365          if (!v && this.useIso) {
22366             v = Date.parseDate(value, 'Y-m-d');
22367         }
22368         if(!v && this.altFormats){
22369             if(!this.altFormatsArray){
22370                 this.altFormatsArray = this.altFormats.split("|");
22371             }
22372             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22373                 v = Date.parseDate(value, this.altFormatsArray[i]);
22374             }
22375         }
22376         return v;
22377     },
22378
22379     // private
22380     formatDate : function(date, fmt){
22381         return (!date || !(date instanceof Date)) ?
22382                date : date.dateFormat(fmt || this.format);
22383     },
22384
22385     // private
22386     menuListeners : {
22387         select: function(m, d){
22388             
22389             this.setValue(d);
22390             this.fireEvent('select', this, d);
22391         },
22392         show : function(){ // retain focus styling
22393             this.onFocus();
22394         },
22395         hide : function(){
22396             this.focus.defer(10, this);
22397             var ml = this.menuListeners;
22398             this.menu.un("select", ml.select,  this);
22399             this.menu.un("show", ml.show,  this);
22400             this.menu.un("hide", ml.hide,  this);
22401         }
22402     },
22403
22404     // private
22405     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22406     onTriggerClick : function(){
22407         if(this.disabled){
22408             return;
22409         }
22410         if(this.menu == null){
22411             this.menu = new Roo.menu.DateMenu();
22412         }
22413         Roo.apply(this.menu.picker,  {
22414             showClear: this.allowBlank,
22415             minDate : this.minValue,
22416             maxDate : this.maxValue,
22417             disabledDatesRE : this.ddMatch,
22418             disabledDatesText : this.disabledDatesText,
22419             disabledDays : this.disabledDays,
22420             disabledDaysText : this.disabledDaysText,
22421             format : this.useIso ? 'Y-m-d' : this.format,
22422             minText : String.format(this.minText, this.formatDate(this.minValue)),
22423             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22424         });
22425         this.menu.on(Roo.apply({}, this.menuListeners, {
22426             scope:this
22427         }));
22428         this.menu.picker.setValue(this.getValue() || new Date());
22429         this.menu.show(this.el, "tl-bl?");
22430     },
22431
22432     beforeBlur : function(){
22433         var v = this.parseDate(this.getRawValue());
22434         if(v){
22435             this.setValue(v);
22436         }
22437     },
22438
22439     /*@
22440      * overide
22441      * 
22442      */
22443     isDirty : function() {
22444         if(this.disabled) {
22445             return false;
22446         }
22447         
22448         if(typeof(this.startValue) === 'undefined'){
22449             return false;
22450         }
22451         
22452         return String(this.getValue()) !== String(this.startValue);
22453         
22454     }
22455 });/*
22456  * Based on:
22457  * Ext JS Library 1.1.1
22458  * Copyright(c) 2006-2007, Ext JS, LLC.
22459  *
22460  * Originally Released Under LGPL - original licence link has changed is not relivant.
22461  *
22462  * Fork - LGPL
22463  * <script type="text/javascript">
22464  */
22465  
22466 /**
22467  * @class Roo.form.MonthField
22468  * @extends Roo.form.TriggerField
22469  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22470 * @constructor
22471 * Create a new MonthField
22472 * @param {Object} config
22473  */
22474 Roo.form.MonthField = function(config){
22475     
22476     Roo.form.MonthField.superclass.constructor.call(this, config);
22477     
22478       this.addEvents({
22479          
22480         /**
22481          * @event select
22482          * Fires when a date is selected
22483              * @param {Roo.form.MonthFieeld} combo This combo box
22484              * @param {Date} date The date selected
22485              */
22486         'select' : true
22487          
22488     });
22489     
22490     
22491     if(typeof this.minValue == "string") {
22492         this.minValue = this.parseDate(this.minValue);
22493     }
22494     if(typeof this.maxValue == "string") {
22495         this.maxValue = this.parseDate(this.maxValue);
22496     }
22497     this.ddMatch = null;
22498     if(this.disabledDates){
22499         var dd = this.disabledDates;
22500         var re = "(?:";
22501         for(var i = 0; i < dd.length; i++){
22502             re += dd[i];
22503             if(i != dd.length-1) {
22504                 re += "|";
22505             }
22506         }
22507         this.ddMatch = new RegExp(re + ")");
22508     }
22509 };
22510
22511 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22512     /**
22513      * @cfg {String} format
22514      * The default date format string which can be overriden for localization support.  The format must be
22515      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22516      */
22517     format : "M Y",
22518     /**
22519      * @cfg {String} altFormats
22520      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22521      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22522      */
22523     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22524     /**
22525      * @cfg {Array} disabledDays
22526      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22527      */
22528     disabledDays : [0,1,2,3,4,5,6],
22529     /**
22530      * @cfg {String} disabledDaysText
22531      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22532      */
22533     disabledDaysText : "Disabled",
22534     /**
22535      * @cfg {Array} disabledDates
22536      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22537      * expression so they are very powerful. Some examples:
22538      * <ul>
22539      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22540      * <li>["03/08", "09/16"] would disable those days for every year</li>
22541      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22542      * <li>["03/../2006"] would disable every day in March 2006</li>
22543      * <li>["^03"] would disable every day in every March</li>
22544      * </ul>
22545      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22546      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22547      */
22548     disabledDates : null,
22549     /**
22550      * @cfg {String} disabledDatesText
22551      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22552      */
22553     disabledDatesText : "Disabled",
22554     /**
22555      * @cfg {Date/String} minValue
22556      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22557      * valid format (defaults to null).
22558      */
22559     minValue : null,
22560     /**
22561      * @cfg {Date/String} maxValue
22562      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22563      * valid format (defaults to null).
22564      */
22565     maxValue : null,
22566     /**
22567      * @cfg {String} minText
22568      * The error text to display when the date in the cell is before minValue (defaults to
22569      * 'The date in this field must be after {minValue}').
22570      */
22571     minText : "The date in this field must be equal to or after {0}",
22572     /**
22573      * @cfg {String} maxTextf
22574      * The error text to display when the date in the cell is after maxValue (defaults to
22575      * 'The date in this field must be before {maxValue}').
22576      */
22577     maxText : "The date in this field must be equal to or before {0}",
22578     /**
22579      * @cfg {String} invalidText
22580      * The error text to display when the date in the field is invalid (defaults to
22581      * '{value} is not a valid date - it must be in the format {format}').
22582      */
22583     invalidText : "{0} is not a valid date - it must be in the format {1}",
22584     /**
22585      * @cfg {String} triggerClass
22586      * An additional CSS class used to style the trigger button.  The trigger will always get the
22587      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22588      * which displays a calendar icon).
22589      */
22590     triggerClass : 'x-form-date-trigger',
22591     
22592
22593     /**
22594      * @cfg {Boolean} useIso
22595      * if enabled, then the date field will use a hidden field to store the 
22596      * real value as iso formated date. default (true)
22597      */ 
22598     useIso : true,
22599     /**
22600      * @cfg {String/Object} autoCreate
22601      * A DomHelper element spec, or true for a default element spec (defaults to
22602      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22603      */ 
22604     // private
22605     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22606     
22607     // private
22608     hiddenField: false,
22609     
22610     hideMonthPicker : false,
22611     
22612     onRender : function(ct, position)
22613     {
22614         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22615         if (this.useIso) {
22616             this.el.dom.removeAttribute('name'); 
22617             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22618                     'before', true);
22619             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22620             // prevent input submission
22621             this.hiddenName = this.name;
22622         }
22623             
22624             
22625     },
22626     
22627     // private
22628     validateValue : function(value)
22629     {
22630         value = this.formatDate(value);
22631         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22632             return false;
22633         }
22634         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22635              return true;
22636         }
22637         var svalue = value;
22638         value = this.parseDate(value);
22639         if(!value){
22640             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22641             return false;
22642         }
22643         var time = value.getTime();
22644         if(this.minValue && time < this.minValue.getTime()){
22645             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22646             return false;
22647         }
22648         if(this.maxValue && time > this.maxValue.getTime()){
22649             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22650             return false;
22651         }
22652         /*if(this.disabledDays){
22653             var day = value.getDay();
22654             for(var i = 0; i < this.disabledDays.length; i++) {
22655                 if(day === this.disabledDays[i]){
22656                     this.markInvalid(this.disabledDaysText);
22657                     return false;
22658                 }
22659             }
22660         }
22661         */
22662         var fvalue = this.formatDate(value);
22663         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22664             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22665             return false;
22666         }
22667         */
22668         return true;
22669     },
22670
22671     // private
22672     // Provides logic to override the default TriggerField.validateBlur which just returns true
22673     validateBlur : function(){
22674         return !this.menu || !this.menu.isVisible();
22675     },
22676
22677     /**
22678      * Returns the current date value of the date field.
22679      * @return {Date} The date value
22680      */
22681     getValue : function(){
22682         
22683         
22684         
22685         return  this.hiddenField ?
22686                 this.hiddenField.value :
22687                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22688     },
22689
22690     /**
22691      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22692      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22693      * (the default format used is "m/d/y").
22694      * <br />Usage:
22695      * <pre><code>
22696 //All of these calls set the same date value (May 4, 2006)
22697
22698 //Pass a date object:
22699 var dt = new Date('5/4/06');
22700 monthField.setValue(dt);
22701
22702 //Pass a date string (default format):
22703 monthField.setValue('5/4/06');
22704
22705 //Pass a date string (custom format):
22706 monthField.format = 'Y-m-d';
22707 monthField.setValue('2006-5-4');
22708 </code></pre>
22709      * @param {String/Date} date The date or valid date string
22710      */
22711     setValue : function(date){
22712         Roo.log('month setValue' + date);
22713         // can only be first of month..
22714         
22715         var val = this.parseDate(date);
22716         
22717         if (this.hiddenField) {
22718             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22719         }
22720         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22721         this.value = this.parseDate(date);
22722     },
22723
22724     // private
22725     parseDate : function(value){
22726         if(!value || value instanceof Date){
22727             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22728             return value;
22729         }
22730         var v = Date.parseDate(value, this.format);
22731         if (!v && this.useIso) {
22732             v = Date.parseDate(value, 'Y-m-d');
22733         }
22734         if (v) {
22735             // 
22736             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22737         }
22738         
22739         
22740         if(!v && this.altFormats){
22741             if(!this.altFormatsArray){
22742                 this.altFormatsArray = this.altFormats.split("|");
22743             }
22744             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22745                 v = Date.parseDate(value, this.altFormatsArray[i]);
22746             }
22747         }
22748         return v;
22749     },
22750
22751     // private
22752     formatDate : function(date, fmt){
22753         return (!date || !(date instanceof Date)) ?
22754                date : date.dateFormat(fmt || this.format);
22755     },
22756
22757     // private
22758     menuListeners : {
22759         select: function(m, d){
22760             this.setValue(d);
22761             this.fireEvent('select', this, d);
22762         },
22763         show : function(){ // retain focus styling
22764             this.onFocus();
22765         },
22766         hide : function(){
22767             this.focus.defer(10, this);
22768             var ml = this.menuListeners;
22769             this.menu.un("select", ml.select,  this);
22770             this.menu.un("show", ml.show,  this);
22771             this.menu.un("hide", ml.hide,  this);
22772         }
22773     },
22774     // private
22775     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22776     onTriggerClick : function(){
22777         if(this.disabled){
22778             return;
22779         }
22780         if(this.menu == null){
22781             this.menu = new Roo.menu.DateMenu();
22782            
22783         }
22784         
22785         Roo.apply(this.menu.picker,  {
22786             
22787             showClear: this.allowBlank,
22788             minDate : this.minValue,
22789             maxDate : this.maxValue,
22790             disabledDatesRE : this.ddMatch,
22791             disabledDatesText : this.disabledDatesText,
22792             
22793             format : this.useIso ? 'Y-m-d' : this.format,
22794             minText : String.format(this.minText, this.formatDate(this.minValue)),
22795             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22796             
22797         });
22798          this.menu.on(Roo.apply({}, this.menuListeners, {
22799             scope:this
22800         }));
22801        
22802         
22803         var m = this.menu;
22804         var p = m.picker;
22805         
22806         // hide month picker get's called when we called by 'before hide';
22807         
22808         var ignorehide = true;
22809         p.hideMonthPicker  = function(disableAnim){
22810             if (ignorehide) {
22811                 return;
22812             }
22813              if(this.monthPicker){
22814                 Roo.log("hideMonthPicker called");
22815                 if(disableAnim === true){
22816                     this.monthPicker.hide();
22817                 }else{
22818                     this.monthPicker.slideOut('t', {duration:.2});
22819                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22820                     p.fireEvent("select", this, this.value);
22821                     m.hide();
22822                 }
22823             }
22824         }
22825         
22826         Roo.log('picker set value');
22827         Roo.log(this.getValue());
22828         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22829         m.show(this.el, 'tl-bl?');
22830         ignorehide  = false;
22831         // this will trigger hideMonthPicker..
22832         
22833         
22834         // hidden the day picker
22835         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22836         
22837         
22838         
22839       
22840         
22841         p.showMonthPicker.defer(100, p);
22842     
22843         
22844        
22845     },
22846
22847     beforeBlur : function(){
22848         var v = this.parseDate(this.getRawValue());
22849         if(v){
22850             this.setValue(v);
22851         }
22852     }
22853
22854     /** @cfg {Boolean} grow @hide */
22855     /** @cfg {Number} growMin @hide */
22856     /** @cfg {Number} growMax @hide */
22857     /**
22858      * @hide
22859      * @method autoSize
22860      */
22861 });/*
22862  * Based on:
22863  * Ext JS Library 1.1.1
22864  * Copyright(c) 2006-2007, Ext JS, LLC.
22865  *
22866  * Originally Released Under LGPL - original licence link has changed is not relivant.
22867  *
22868  * Fork - LGPL
22869  * <script type="text/javascript">
22870  */
22871  
22872
22873 /**
22874  * @class Roo.form.ComboBox
22875  * @extends Roo.form.TriggerField
22876  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22877  * @constructor
22878  * Create a new ComboBox.
22879  * @param {Object} config Configuration options
22880  */
22881 Roo.form.ComboBox = function(config){
22882     Roo.form.ComboBox.superclass.constructor.call(this, config);
22883     this.addEvents({
22884         /**
22885          * @event expand
22886          * Fires when the dropdown list is expanded
22887              * @param {Roo.form.ComboBox} combo This combo box
22888              */
22889         'expand' : true,
22890         /**
22891          * @event collapse
22892          * Fires when the dropdown list is collapsed
22893              * @param {Roo.form.ComboBox} combo This combo box
22894              */
22895         'collapse' : true,
22896         /**
22897          * @event beforeselect
22898          * Fires before a list item is selected. Return false to cancel the selection.
22899              * @param {Roo.form.ComboBox} combo This combo box
22900              * @param {Roo.data.Record} record The data record returned from the underlying store
22901              * @param {Number} index The index of the selected item in the dropdown list
22902              */
22903         'beforeselect' : true,
22904         /**
22905          * @event select
22906          * Fires when a list item is selected
22907              * @param {Roo.form.ComboBox} combo This combo box
22908              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22909              * @param {Number} index The index of the selected item in the dropdown list
22910              */
22911         'select' : true,
22912         /**
22913          * @event beforequery
22914          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22915          * The event object passed has these properties:
22916              * @param {Roo.form.ComboBox} combo This combo box
22917              * @param {String} query The query
22918              * @param {Boolean} forceAll true to force "all" query
22919              * @param {Boolean} cancel true to cancel the query
22920              * @param {Object} e The query event object
22921              */
22922         'beforequery': true,
22923          /**
22924          * @event add
22925          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22926              * @param {Roo.form.ComboBox} combo This combo box
22927              */
22928         'add' : true,
22929         /**
22930          * @event edit
22931          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22932              * @param {Roo.form.ComboBox} combo This combo box
22933              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22934              */
22935         'edit' : true
22936         
22937         
22938     });
22939     if(this.transform){
22940         this.allowDomMove = false;
22941         var s = Roo.getDom(this.transform);
22942         if(!this.hiddenName){
22943             this.hiddenName = s.name;
22944         }
22945         if(!this.store){
22946             this.mode = 'local';
22947             var d = [], opts = s.options;
22948             for(var i = 0, len = opts.length;i < len; i++){
22949                 var o = opts[i];
22950                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22951                 if(o.selected) {
22952                     this.value = value;
22953                 }
22954                 d.push([value, o.text]);
22955             }
22956             this.store = new Roo.data.SimpleStore({
22957                 'id': 0,
22958                 fields: ['value', 'text'],
22959                 data : d
22960             });
22961             this.valueField = 'value';
22962             this.displayField = 'text';
22963         }
22964         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22965         if(!this.lazyRender){
22966             this.target = true;
22967             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22968             s.parentNode.removeChild(s); // remove it
22969             this.render(this.el.parentNode);
22970         }else{
22971             s.parentNode.removeChild(s); // remove it
22972         }
22973
22974     }
22975     if (this.store) {
22976         this.store = Roo.factory(this.store, Roo.data);
22977     }
22978     
22979     this.selectedIndex = -1;
22980     if(this.mode == 'local'){
22981         if(config.queryDelay === undefined){
22982             this.queryDelay = 10;
22983         }
22984         if(config.minChars === undefined){
22985             this.minChars = 0;
22986         }
22987     }
22988 };
22989
22990 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22991     /**
22992      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22993      */
22994     /**
22995      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22996      * rendering into an Roo.Editor, defaults to false)
22997      */
22998     /**
22999      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23000      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23001      */
23002     /**
23003      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23004      */
23005     /**
23006      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23007      * the dropdown list (defaults to undefined, with no header element)
23008      */
23009
23010      /**
23011      * @cfg {String/Roo.Template} tpl The template to use to render the output
23012      */
23013      
23014     // private
23015     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23016     /**
23017      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23018      */
23019     listWidth: undefined,
23020     /**
23021      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23022      * mode = 'remote' or 'text' if mode = 'local')
23023      */
23024     displayField: undefined,
23025     /**
23026      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23027      * mode = 'remote' or 'value' if mode = 'local'). 
23028      * Note: use of a valueField requires the user make a selection
23029      * in order for a value to be mapped.
23030      */
23031     valueField: undefined,
23032     
23033     
23034     /**
23035      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23036      * field's data value (defaults to the underlying DOM element's name)
23037      */
23038     hiddenName: undefined,
23039     /**
23040      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23041      */
23042     listClass: '',
23043     /**
23044      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23045      */
23046     selectedClass: 'x-combo-selected',
23047     /**
23048      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23049      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23050      * which displays a downward arrow icon).
23051      */
23052     triggerClass : 'x-form-arrow-trigger',
23053     /**
23054      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23055      */
23056     shadow:'sides',
23057     /**
23058      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23059      * anchor positions (defaults to 'tl-bl')
23060      */
23061     listAlign: 'tl-bl?',
23062     /**
23063      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23064      */
23065     maxHeight: 300,
23066     /**
23067      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23068      * query specified by the allQuery config option (defaults to 'query')
23069      */
23070     triggerAction: 'query',
23071     /**
23072      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23073      * (defaults to 4, does not apply if editable = false)
23074      */
23075     minChars : 4,
23076     /**
23077      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23078      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23079      */
23080     typeAhead: false,
23081     /**
23082      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23083      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23084      */
23085     queryDelay: 500,
23086     /**
23087      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23088      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23089      */
23090     pageSize: 0,
23091     /**
23092      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23093      * when editable = true (defaults to false)
23094      */
23095     selectOnFocus:false,
23096     /**
23097      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23098      */
23099     queryParam: 'query',
23100     /**
23101      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23102      * when mode = 'remote' (defaults to 'Loading...')
23103      */
23104     loadingText: 'Loading...',
23105     /**
23106      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23107      */
23108     resizable: false,
23109     /**
23110      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23111      */
23112     handleHeight : 8,
23113     /**
23114      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23115      * traditional select (defaults to true)
23116      */
23117     editable: true,
23118     /**
23119      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23120      */
23121     allQuery: '',
23122     /**
23123      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23124      */
23125     mode: 'remote',
23126     /**
23127      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23128      * listWidth has a higher value)
23129      */
23130     minListWidth : 70,
23131     /**
23132      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23133      * allow the user to set arbitrary text into the field (defaults to false)
23134      */
23135     forceSelection:false,
23136     /**
23137      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23138      * if typeAhead = true (defaults to 250)
23139      */
23140     typeAheadDelay : 250,
23141     /**
23142      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23143      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23144      */
23145     valueNotFoundText : undefined,
23146     /**
23147      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23148      */
23149     blockFocus : false,
23150     
23151     /**
23152      * @cfg {Boolean} disableClear Disable showing of clear button.
23153      */
23154     disableClear : false,
23155     /**
23156      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23157      */
23158     alwaysQuery : false,
23159     
23160     //private
23161     addicon : false,
23162     editicon: false,
23163     
23164     // element that contains real text value.. (when hidden is used..)
23165      
23166     // private
23167     onRender : function(ct, position){
23168         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23169         if(this.hiddenName){
23170             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23171                     'before', true);
23172             this.hiddenField.value =
23173                 this.hiddenValue !== undefined ? this.hiddenValue :
23174                 this.value !== undefined ? this.value : '';
23175
23176             // prevent input submission
23177             this.el.dom.removeAttribute('name');
23178              
23179              
23180         }
23181         if(Roo.isGecko){
23182             this.el.dom.setAttribute('autocomplete', 'off');
23183         }
23184
23185         var cls = 'x-combo-list';
23186
23187         this.list = new Roo.Layer({
23188             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23189         });
23190
23191         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23192         this.list.setWidth(lw);
23193         this.list.swallowEvent('mousewheel');
23194         this.assetHeight = 0;
23195
23196         if(this.title){
23197             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23198             this.assetHeight += this.header.getHeight();
23199         }
23200
23201         this.innerList = this.list.createChild({cls:cls+'-inner'});
23202         this.innerList.on('mouseover', this.onViewOver, this);
23203         this.innerList.on('mousemove', this.onViewMove, this);
23204         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23205         
23206         if(this.allowBlank && !this.pageSize && !this.disableClear){
23207             this.footer = this.list.createChild({cls:cls+'-ft'});
23208             this.pageTb = new Roo.Toolbar(this.footer);
23209            
23210         }
23211         if(this.pageSize){
23212             this.footer = this.list.createChild({cls:cls+'-ft'});
23213             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23214                     {pageSize: this.pageSize});
23215             
23216         }
23217         
23218         if (this.pageTb && this.allowBlank && !this.disableClear) {
23219             var _this = this;
23220             this.pageTb.add(new Roo.Toolbar.Fill(), {
23221                 cls: 'x-btn-icon x-btn-clear',
23222                 text: '&#160;',
23223                 handler: function()
23224                 {
23225                     _this.collapse();
23226                     _this.clearValue();
23227                     _this.onSelect(false, -1);
23228                 }
23229             });
23230         }
23231         if (this.footer) {
23232             this.assetHeight += this.footer.getHeight();
23233         }
23234         
23235
23236         if(!this.tpl){
23237             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23238         }
23239
23240         this.view = new Roo.View(this.innerList, this.tpl, {
23241             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23242         });
23243
23244         this.view.on('click', this.onViewClick, this);
23245
23246         this.store.on('beforeload', this.onBeforeLoad, this);
23247         this.store.on('load', this.onLoad, this);
23248         this.store.on('loadexception', this.onLoadException, this);
23249
23250         if(this.resizable){
23251             this.resizer = new Roo.Resizable(this.list,  {
23252                pinned:true, handles:'se'
23253             });
23254             this.resizer.on('resize', function(r, w, h){
23255                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23256                 this.listWidth = w;
23257                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23258                 this.restrictHeight();
23259             }, this);
23260             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23261         }
23262         if(!this.editable){
23263             this.editable = true;
23264             this.setEditable(false);
23265         }  
23266         
23267         
23268         if (typeof(this.events.add.listeners) != 'undefined') {
23269             
23270             this.addicon = this.wrap.createChild(
23271                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23272        
23273             this.addicon.on('click', function(e) {
23274                 this.fireEvent('add', this);
23275             }, this);
23276         }
23277         if (typeof(this.events.edit.listeners) != 'undefined') {
23278             
23279             this.editicon = this.wrap.createChild(
23280                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23281             if (this.addicon) {
23282                 this.editicon.setStyle('margin-left', '40px');
23283             }
23284             this.editicon.on('click', function(e) {
23285                 
23286                 // we fire even  if inothing is selected..
23287                 this.fireEvent('edit', this, this.lastData );
23288                 
23289             }, this);
23290         }
23291         
23292         
23293         
23294     },
23295
23296     // private
23297     initEvents : function(){
23298         Roo.form.ComboBox.superclass.initEvents.call(this);
23299
23300         this.keyNav = new Roo.KeyNav(this.el, {
23301             "up" : function(e){
23302                 this.inKeyMode = true;
23303                 this.selectPrev();
23304             },
23305
23306             "down" : function(e){
23307                 if(!this.isExpanded()){
23308                     this.onTriggerClick();
23309                 }else{
23310                     this.inKeyMode = true;
23311                     this.selectNext();
23312                 }
23313             },
23314
23315             "enter" : function(e){
23316                 this.onViewClick();
23317                 //return true;
23318             },
23319
23320             "esc" : function(e){
23321                 this.collapse();
23322             },
23323
23324             "tab" : function(e){
23325                 this.onViewClick(false);
23326                 this.fireEvent("specialkey", this, e);
23327                 return true;
23328             },
23329
23330             scope : this,
23331
23332             doRelay : function(foo, bar, hname){
23333                 if(hname == 'down' || this.scope.isExpanded()){
23334                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23335                 }
23336                 return true;
23337             },
23338
23339             forceKeyDown: true
23340         });
23341         this.queryDelay = Math.max(this.queryDelay || 10,
23342                 this.mode == 'local' ? 10 : 250);
23343         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23344         if(this.typeAhead){
23345             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23346         }
23347         if(this.editable !== false){
23348             this.el.on("keyup", this.onKeyUp, this);
23349         }
23350         if(this.forceSelection){
23351             this.on('blur', this.doForce, this);
23352         }
23353     },
23354
23355     onDestroy : function(){
23356         if(this.view){
23357             this.view.setStore(null);
23358             this.view.el.removeAllListeners();
23359             this.view.el.remove();
23360             this.view.purgeListeners();
23361         }
23362         if(this.list){
23363             this.list.destroy();
23364         }
23365         if(this.store){
23366             this.store.un('beforeload', this.onBeforeLoad, this);
23367             this.store.un('load', this.onLoad, this);
23368             this.store.un('loadexception', this.onLoadException, this);
23369         }
23370         Roo.form.ComboBox.superclass.onDestroy.call(this);
23371     },
23372
23373     // private
23374     fireKey : function(e){
23375         if(e.isNavKeyPress() && !this.list.isVisible()){
23376             this.fireEvent("specialkey", this, e);
23377         }
23378     },
23379
23380     // private
23381     onResize: function(w, h){
23382         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23383         
23384         if(typeof w != 'number'){
23385             // we do not handle it!?!?
23386             return;
23387         }
23388         var tw = this.trigger.getWidth();
23389         tw += this.addicon ? this.addicon.getWidth() : 0;
23390         tw += this.editicon ? this.editicon.getWidth() : 0;
23391         var x = w - tw;
23392         this.el.setWidth( this.adjustWidth('input', x));
23393             
23394         this.trigger.setStyle('left', x+'px');
23395         
23396         if(this.list && this.listWidth === undefined){
23397             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23398             this.list.setWidth(lw);
23399             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23400         }
23401         
23402     
23403         
23404     },
23405
23406     /**
23407      * Allow or prevent the user from directly editing the field text.  If false is passed,
23408      * the user will only be able to select from the items defined in the dropdown list.  This method
23409      * is the runtime equivalent of setting the 'editable' config option at config time.
23410      * @param {Boolean} value True to allow the user to directly edit the field text
23411      */
23412     setEditable : function(value){
23413         if(value == this.editable){
23414             return;
23415         }
23416         this.editable = value;
23417         if(!value){
23418             this.el.dom.setAttribute('readOnly', true);
23419             this.el.on('mousedown', this.onTriggerClick,  this);
23420             this.el.addClass('x-combo-noedit');
23421         }else{
23422             this.el.dom.setAttribute('readOnly', false);
23423             this.el.un('mousedown', this.onTriggerClick,  this);
23424             this.el.removeClass('x-combo-noedit');
23425         }
23426     },
23427
23428     // private
23429     onBeforeLoad : function(){
23430         if(!this.hasFocus){
23431             return;
23432         }
23433         this.innerList.update(this.loadingText ?
23434                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23435         this.restrictHeight();
23436         this.selectedIndex = -1;
23437     },
23438
23439     // private
23440     onLoad : function(){
23441         if(!this.hasFocus){
23442             return;
23443         }
23444         if(this.store.getCount() > 0){
23445             this.expand();
23446             this.restrictHeight();
23447             if(this.lastQuery == this.allQuery){
23448                 if(this.editable){
23449                     this.el.dom.select();
23450                 }
23451                 if(!this.selectByValue(this.value, true)){
23452                     this.select(0, true);
23453                 }
23454             }else{
23455                 this.selectNext();
23456                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23457                     this.taTask.delay(this.typeAheadDelay);
23458                 }
23459             }
23460         }else{
23461             this.onEmptyResults();
23462         }
23463         //this.el.focus();
23464     },
23465     // private
23466     onLoadException : function()
23467     {
23468         this.collapse();
23469         Roo.log(this.store.reader.jsonData);
23470         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23471             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23472         }
23473         
23474         
23475     },
23476     // private
23477     onTypeAhead : function(){
23478         if(this.store.getCount() > 0){
23479             var r = this.store.getAt(0);
23480             var newValue = r.data[this.displayField];
23481             var len = newValue.length;
23482             var selStart = this.getRawValue().length;
23483             if(selStart != len){
23484                 this.setRawValue(newValue);
23485                 this.selectText(selStart, newValue.length);
23486             }
23487         }
23488     },
23489
23490     // private
23491     onSelect : function(record, index){
23492         if(this.fireEvent('beforeselect', this, record, index) !== false){
23493             this.setFromData(index > -1 ? record.data : false);
23494             this.collapse();
23495             this.fireEvent('select', this, record, index);
23496         }
23497     },
23498
23499     /**
23500      * Returns the currently selected field value or empty string if no value is set.
23501      * @return {String} value The selected value
23502      */
23503     getValue : function(){
23504         if(this.valueField){
23505             return typeof this.value != 'undefined' ? this.value : '';
23506         }
23507         return Roo.form.ComboBox.superclass.getValue.call(this);
23508     },
23509
23510     /**
23511      * Clears any text/value currently set in the field
23512      */
23513     clearValue : function(){
23514         if(this.hiddenField){
23515             this.hiddenField.value = '';
23516         }
23517         this.value = '';
23518         this.setRawValue('');
23519         this.lastSelectionText = '';
23520         
23521     },
23522
23523     /**
23524      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23525      * will be displayed in the field.  If the value does not match the data value of an existing item,
23526      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23527      * Otherwise the field will be blank (although the value will still be set).
23528      * @param {String} value The value to match
23529      */
23530     setValue : function(v){
23531         var text = v;
23532         if(this.valueField){
23533             var r = this.findRecord(this.valueField, v);
23534             if(r){
23535                 text = r.data[this.displayField];
23536             }else if(this.valueNotFoundText !== undefined){
23537                 text = this.valueNotFoundText;
23538             }
23539         }
23540         this.lastSelectionText = text;
23541         if(this.hiddenField){
23542             this.hiddenField.value = v;
23543         }
23544         Roo.form.ComboBox.superclass.setValue.call(this, text);
23545         this.value = v;
23546     },
23547     /**
23548      * @property {Object} the last set data for the element
23549      */
23550     
23551     lastData : false,
23552     /**
23553      * Sets the value of the field based on a object which is related to the record format for the store.
23554      * @param {Object} value the value to set as. or false on reset?
23555      */
23556     setFromData : function(o){
23557         var dv = ''; // display value
23558         var vv = ''; // value value..
23559         this.lastData = o;
23560         if (this.displayField) {
23561             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23562         } else {
23563             // this is an error condition!!!
23564             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23565         }
23566         
23567         if(this.valueField){
23568             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23569         }
23570         if(this.hiddenField){
23571             this.hiddenField.value = vv;
23572             
23573             this.lastSelectionText = dv;
23574             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23575             this.value = vv;
23576             return;
23577         }
23578         // no hidden field.. - we store the value in 'value', but still display
23579         // display field!!!!
23580         this.lastSelectionText = dv;
23581         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23582         this.value = vv;
23583         
23584         
23585     },
23586     // private
23587     reset : function(){
23588         // overridden so that last data is reset..
23589         this.setValue(this.resetValue);
23590         this.clearInvalid();
23591         this.lastData = false;
23592         if (this.view) {
23593             this.view.clearSelections();
23594         }
23595     },
23596     // private
23597     findRecord : function(prop, value){
23598         var record;
23599         if(this.store.getCount() > 0){
23600             this.store.each(function(r){
23601                 if(r.data[prop] == value){
23602                     record = r;
23603                     return false;
23604                 }
23605                 return true;
23606             });
23607         }
23608         return record;
23609     },
23610     
23611     getName: function()
23612     {
23613         // returns hidden if it's set..
23614         if (!this.rendered) {return ''};
23615         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23616         
23617     },
23618     // private
23619     onViewMove : function(e, t){
23620         this.inKeyMode = false;
23621     },
23622
23623     // private
23624     onViewOver : function(e, t){
23625         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23626             return;
23627         }
23628         var item = this.view.findItemFromChild(t);
23629         if(item){
23630             var index = this.view.indexOf(item);
23631             this.select(index, false);
23632         }
23633     },
23634
23635     // private
23636     onViewClick : function(doFocus)
23637     {
23638         var index = this.view.getSelectedIndexes()[0];
23639         var r = this.store.getAt(index);
23640         if(r){
23641             this.onSelect(r, index);
23642         }
23643         if(doFocus !== false && !this.blockFocus){
23644             this.el.focus();
23645         }
23646     },
23647
23648     // private
23649     restrictHeight : function(){
23650         this.innerList.dom.style.height = '';
23651         var inner = this.innerList.dom;
23652         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23653         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23654         this.list.beginUpdate();
23655         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23656         this.list.alignTo(this.el, this.listAlign);
23657         this.list.endUpdate();
23658     },
23659
23660     // private
23661     onEmptyResults : function(){
23662         this.collapse();
23663     },
23664
23665     /**
23666      * Returns true if the dropdown list is expanded, else false.
23667      */
23668     isExpanded : function(){
23669         return this.list.isVisible();
23670     },
23671
23672     /**
23673      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23674      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23675      * @param {String} value The data value of the item to select
23676      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23677      * selected item if it is not currently in view (defaults to true)
23678      * @return {Boolean} True if the value matched an item in the list, else false
23679      */
23680     selectByValue : function(v, scrollIntoView){
23681         if(v !== undefined && v !== null){
23682             var r = this.findRecord(this.valueField || this.displayField, v);
23683             if(r){
23684                 this.select(this.store.indexOf(r), scrollIntoView);
23685                 return true;
23686             }
23687         }
23688         return false;
23689     },
23690
23691     /**
23692      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23693      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23694      * @param {Number} index The zero-based index of the list item to select
23695      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23696      * selected item if it is not currently in view (defaults to true)
23697      */
23698     select : function(index, scrollIntoView){
23699         this.selectedIndex = index;
23700         this.view.select(index);
23701         if(scrollIntoView !== false){
23702             var el = this.view.getNode(index);
23703             if(el){
23704                 this.innerList.scrollChildIntoView(el, false);
23705             }
23706         }
23707     },
23708
23709     // private
23710     selectNext : function(){
23711         var ct = this.store.getCount();
23712         if(ct > 0){
23713             if(this.selectedIndex == -1){
23714                 this.select(0);
23715             }else if(this.selectedIndex < ct-1){
23716                 this.select(this.selectedIndex+1);
23717             }
23718         }
23719     },
23720
23721     // private
23722     selectPrev : function(){
23723         var ct = this.store.getCount();
23724         if(ct > 0){
23725             if(this.selectedIndex == -1){
23726                 this.select(0);
23727             }else if(this.selectedIndex != 0){
23728                 this.select(this.selectedIndex-1);
23729             }
23730         }
23731     },
23732
23733     // private
23734     onKeyUp : function(e){
23735         if(this.editable !== false && !e.isSpecialKey()){
23736             this.lastKey = e.getKey();
23737             this.dqTask.delay(this.queryDelay);
23738         }
23739     },
23740
23741     // private
23742     validateBlur : function(){
23743         return !this.list || !this.list.isVisible();   
23744     },
23745
23746     // private
23747     initQuery : function(){
23748         this.doQuery(this.getRawValue());
23749     },
23750
23751     // private
23752     doForce : function(){
23753         if(this.el.dom.value.length > 0){
23754             this.el.dom.value =
23755                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23756              
23757         }
23758     },
23759
23760     /**
23761      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23762      * query allowing the query action to be canceled if needed.
23763      * @param {String} query The SQL query to execute
23764      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23765      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23766      * saved in the current store (defaults to false)
23767      */
23768     doQuery : function(q, forceAll){
23769         if(q === undefined || q === null){
23770             q = '';
23771         }
23772         var qe = {
23773             query: q,
23774             forceAll: forceAll,
23775             combo: this,
23776             cancel:false
23777         };
23778         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23779             return false;
23780         }
23781         q = qe.query;
23782         forceAll = qe.forceAll;
23783         if(forceAll === true || (q.length >= this.minChars)){
23784             if(this.lastQuery != q || this.alwaysQuery){
23785                 this.lastQuery = q;
23786                 if(this.mode == 'local'){
23787                     this.selectedIndex = -1;
23788                     if(forceAll){
23789                         this.store.clearFilter();
23790                     }else{
23791                         this.store.filter(this.displayField, q);
23792                     }
23793                     this.onLoad();
23794                 }else{
23795                     this.store.baseParams[this.queryParam] = q;
23796                     this.store.load({
23797                         params: this.getParams(q)
23798                     });
23799                     this.expand();
23800                 }
23801             }else{
23802                 this.selectedIndex = -1;
23803                 this.onLoad();   
23804             }
23805         }
23806     },
23807
23808     // private
23809     getParams : function(q){
23810         var p = {};
23811         //p[this.queryParam] = q;
23812         if(this.pageSize){
23813             p.start = 0;
23814             p.limit = this.pageSize;
23815         }
23816         return p;
23817     },
23818
23819     /**
23820      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23821      */
23822     collapse : function(){
23823         if(!this.isExpanded()){
23824             return;
23825         }
23826         this.list.hide();
23827         Roo.get(document).un('mousedown', this.collapseIf, this);
23828         Roo.get(document).un('mousewheel', this.collapseIf, this);
23829         if (!this.editable) {
23830             Roo.get(document).un('keydown', this.listKeyPress, this);
23831         }
23832         this.fireEvent('collapse', this);
23833     },
23834
23835     // private
23836     collapseIf : function(e){
23837         if(!e.within(this.wrap) && !e.within(this.list)){
23838             this.collapse();
23839         }
23840     },
23841
23842     /**
23843      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23844      */
23845     expand : function(){
23846         if(this.isExpanded() || !this.hasFocus){
23847             return;
23848         }
23849         this.list.alignTo(this.el, this.listAlign);
23850         this.list.show();
23851         Roo.get(document).on('mousedown', this.collapseIf, this);
23852         Roo.get(document).on('mousewheel', this.collapseIf, this);
23853         if (!this.editable) {
23854             Roo.get(document).on('keydown', this.listKeyPress, this);
23855         }
23856         
23857         this.fireEvent('expand', this);
23858     },
23859
23860     // private
23861     // Implements the default empty TriggerField.onTriggerClick function
23862     onTriggerClick : function(){
23863         if(this.disabled){
23864             return;
23865         }
23866         if(this.isExpanded()){
23867             this.collapse();
23868             if (!this.blockFocus) {
23869                 this.el.focus();
23870             }
23871             
23872         }else {
23873             this.hasFocus = true;
23874             if(this.triggerAction == 'all') {
23875                 this.doQuery(this.allQuery, true);
23876             } else {
23877                 this.doQuery(this.getRawValue());
23878             }
23879             if (!this.blockFocus) {
23880                 this.el.focus();
23881             }
23882         }
23883     },
23884     listKeyPress : function(e)
23885     {
23886         //Roo.log('listkeypress');
23887         // scroll to first matching element based on key pres..
23888         if (e.isSpecialKey()) {
23889             return false;
23890         }
23891         var k = String.fromCharCode(e.getKey()).toUpperCase();
23892         //Roo.log(k);
23893         var match  = false;
23894         var csel = this.view.getSelectedNodes();
23895         var cselitem = false;
23896         if (csel.length) {
23897             var ix = this.view.indexOf(csel[0]);
23898             cselitem  = this.store.getAt(ix);
23899             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23900                 cselitem = false;
23901             }
23902             
23903         }
23904         
23905         this.store.each(function(v) { 
23906             if (cselitem) {
23907                 // start at existing selection.
23908                 if (cselitem.id == v.id) {
23909                     cselitem = false;
23910                 }
23911                 return;
23912             }
23913                 
23914             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23915                 match = this.store.indexOf(v);
23916                 return false;
23917             }
23918         }, this);
23919         
23920         if (match === false) {
23921             return true; // no more action?
23922         }
23923         // scroll to?
23924         this.view.select(match);
23925         var sn = Roo.get(this.view.getSelectedNodes()[0]);
23926         sn.scrollIntoView(sn.dom.parentNode, false);
23927     }
23928
23929     /** 
23930     * @cfg {Boolean} grow 
23931     * @hide 
23932     */
23933     /** 
23934     * @cfg {Number} growMin 
23935     * @hide 
23936     */
23937     /** 
23938     * @cfg {Number} growMax 
23939     * @hide 
23940     */
23941     /**
23942      * @hide
23943      * @method autoSize
23944      */
23945 });/*
23946  * Copyright(c) 2010-2012, Roo J Solutions Limited
23947  *
23948  * Licence LGPL
23949  *
23950  */
23951
23952 /**
23953  * @class Roo.form.ComboBoxArray
23954  * @extends Roo.form.TextField
23955  * A facebook style adder... for lists of email / people / countries  etc...
23956  * pick multiple items from a combo box, and shows each one.
23957  *
23958  *  Fred [x]  Brian [x]  [Pick another |v]
23959  *
23960  *
23961  *  For this to work: it needs various extra information
23962  *    - normal combo problay has
23963  *      name, hiddenName
23964  *    + displayField, valueField
23965  *
23966  *    For our purpose...
23967  *
23968  *
23969  *   If we change from 'extends' to wrapping...
23970  *   
23971  *  
23972  *
23973  
23974  
23975  * @constructor
23976  * Create a new ComboBoxArray.
23977  * @param {Object} config Configuration options
23978  */
23979  
23980
23981 Roo.form.ComboBoxArray = function(config)
23982 {
23983     this.addEvents({
23984         /**
23985          * @event beforeremove
23986          * Fires before remove the value from the list
23987              * @param {Roo.form.ComboBoxArray} _self This combo box array
23988              * @param {Roo.form.ComboBoxArray.Item} item removed item
23989              */
23990         'beforeremove' : true,
23991         /**
23992          * @event remove
23993          * Fires when remove the value from the list
23994              * @param {Roo.form.ComboBoxArray} _self This combo box array
23995              * @param {Roo.form.ComboBoxArray.Item} item removed item
23996              */
23997         'remove' : true
23998         
23999         
24000     });
24001     
24002     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24003     
24004     this.items = new Roo.util.MixedCollection(false);
24005     
24006     // construct the child combo...
24007     
24008     
24009     
24010     
24011    
24012     
24013 }
24014
24015  
24016 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24017
24018     /**
24019      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24020      */
24021     
24022     lastData : false,
24023     
24024     // behavies liek a hiddne field
24025     inputType:      'hidden',
24026     /**
24027      * @cfg {Number} width The width of the box that displays the selected element
24028      */ 
24029     width:          300,
24030
24031     
24032     
24033     /**
24034      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24035      */
24036     name : false,
24037     /**
24038      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24039      */
24040     hiddenName : false,
24041     
24042     
24043     // private the array of items that are displayed..
24044     items  : false,
24045     // private - the hidden field el.
24046     hiddenEl : false,
24047     // private - the filed el..
24048     el : false,
24049     
24050     //validateValue : function() { return true; }, // all values are ok!
24051     //onAddClick: function() { },
24052     
24053     onRender : function(ct, position) 
24054     {
24055         
24056         // create the standard hidden element
24057         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24058         
24059         
24060         // give fake names to child combo;
24061         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24062         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24063         
24064         this.combo = Roo.factory(this.combo, Roo.form);
24065         this.combo.onRender(ct, position);
24066         if (typeof(this.combo.width) != 'undefined') {
24067             this.combo.onResize(this.combo.width,0);
24068         }
24069         
24070         this.combo.initEvents();
24071         
24072         // assigned so form know we need to do this..
24073         this.store          = this.combo.store;
24074         this.valueField     = this.combo.valueField;
24075         this.displayField   = this.combo.displayField ;
24076         
24077         
24078         this.combo.wrap.addClass('x-cbarray-grp');
24079         
24080         var cbwrap = this.combo.wrap.createChild(
24081             {tag: 'div', cls: 'x-cbarray-cb'},
24082             this.combo.el.dom
24083         );
24084         
24085              
24086         this.hiddenEl = this.combo.wrap.createChild({
24087             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24088         });
24089         this.el = this.combo.wrap.createChild({
24090             tag: 'input',  type:'hidden' , name: this.name, value : ''
24091         });
24092          //   this.el.dom.removeAttribute("name");
24093         
24094         
24095         this.outerWrap = this.combo.wrap;
24096         this.wrap = cbwrap;
24097         
24098         this.outerWrap.setWidth(this.width);
24099         this.outerWrap.dom.removeChild(this.el.dom);
24100         
24101         this.wrap.dom.appendChild(this.el.dom);
24102         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24103         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24104         
24105         this.combo.trigger.setStyle('position','relative');
24106         this.combo.trigger.setStyle('left', '0px');
24107         this.combo.trigger.setStyle('top', '2px');
24108         
24109         this.combo.el.setStyle('vertical-align', 'text-bottom');
24110         
24111         //this.trigger.setStyle('vertical-align', 'top');
24112         
24113         // this should use the code from combo really... on('add' ....)
24114         if (this.adder) {
24115             
24116         
24117             this.adder = this.outerWrap.createChild(
24118                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24119             var _t = this;
24120             this.adder.on('click', function(e) {
24121                 _t.fireEvent('adderclick', this, e);
24122             }, _t);
24123         }
24124         //var _t = this;
24125         //this.adder.on('click', this.onAddClick, _t);
24126         
24127         
24128         this.combo.on('select', function(cb, rec, ix) {
24129             this.addItem(rec.data);
24130             
24131             cb.setValue('');
24132             cb.el.dom.value = '';
24133             //cb.lastData = rec.data;
24134             // add to list
24135             
24136         }, this);
24137         
24138         
24139     },
24140     
24141     
24142     getName: function()
24143     {
24144         // returns hidden if it's set..
24145         if (!this.rendered) {return ''};
24146         return  this.hiddenName ? this.hiddenName : this.name;
24147         
24148     },
24149     
24150     
24151     onResize: function(w, h){
24152         
24153         return;
24154         // not sure if this is needed..
24155         //this.combo.onResize(w,h);
24156         
24157         if(typeof w != 'number'){
24158             // we do not handle it!?!?
24159             return;
24160         }
24161         var tw = this.combo.trigger.getWidth();
24162         tw += this.addicon ? this.addicon.getWidth() : 0;
24163         tw += this.editicon ? this.editicon.getWidth() : 0;
24164         var x = w - tw;
24165         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24166             
24167         this.combo.trigger.setStyle('left', '0px');
24168         
24169         if(this.list && this.listWidth === undefined){
24170             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24171             this.list.setWidth(lw);
24172             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24173         }
24174         
24175     
24176         
24177     },
24178     
24179     addItem: function(rec)
24180     {
24181         var valueField = this.combo.valueField;
24182         var displayField = this.combo.displayField;
24183         if (this.items.indexOfKey(rec[valueField]) > -1) {
24184             //console.log("GOT " + rec.data.id);
24185             return;
24186         }
24187         
24188         var x = new Roo.form.ComboBoxArray.Item({
24189             //id : rec[this.idField],
24190             data : rec,
24191             displayField : displayField ,
24192             tipField : displayField ,
24193             cb : this
24194         });
24195         // use the 
24196         this.items.add(rec[valueField],x);
24197         // add it before the element..
24198         this.updateHiddenEl();
24199         x.render(this.outerWrap, this.wrap.dom);
24200         // add the image handler..
24201     },
24202     
24203     updateHiddenEl : function()
24204     {
24205         this.validate();
24206         if (!this.hiddenEl) {
24207             return;
24208         }
24209         var ar = [];
24210         var idField = this.combo.valueField;
24211         
24212         this.items.each(function(f) {
24213             ar.push(f.data[idField]);
24214            
24215         });
24216         this.hiddenEl.dom.value = ar.join(',');
24217         this.validate();
24218     },
24219     
24220     reset : function()
24221     {
24222         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24223         this.items.each(function(f) {
24224            f.remove(); 
24225         });
24226         this.el.dom.value = '';
24227         if (this.hiddenEl) {
24228             this.hiddenEl.dom.value = '';
24229         }
24230         
24231     },
24232     getValue: function()
24233     {
24234         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24235     },
24236     setValue: function(v) // not a valid action - must use addItems..
24237     {
24238          
24239         this.reset();
24240         
24241         
24242         
24243         if (this.store.isLocal && (typeof(v) == 'string')) {
24244             // then we can use the store to find the values..
24245             // comma seperated at present.. this needs to allow JSON based encoding..
24246             this.hiddenEl.value  = v;
24247             var v_ar = [];
24248             Roo.each(v.split(','), function(k) {
24249                 Roo.log("CHECK " + this.valueField + ',' + k);
24250                 var li = this.store.query(this.valueField, k);
24251                 if (!li.length) {
24252                     return;
24253                 }
24254                 var add = {};
24255                 add[this.valueField] = k;
24256                 add[this.displayField] = li.item(0).data[this.displayField];
24257                 
24258                 this.addItem(add);
24259             }, this) 
24260              
24261         }
24262         if (typeof(v) == 'object' ) {
24263             // then let's assume it's an array of objects..
24264             Roo.each(v, function(l) {
24265                 this.addItem(l);
24266             }, this);
24267              
24268         }
24269         
24270         
24271     },
24272     setFromData: function(v)
24273     {
24274         // this recieves an object, if setValues is called.
24275         this.reset();
24276         this.el.dom.value = v[this.displayField];
24277         this.hiddenEl.dom.value = v[this.valueField];
24278         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24279             return;
24280         }
24281         var kv = v[this.valueField];
24282         var dv = v[this.displayField];
24283         kv = typeof(kv) != 'string' ? '' : kv;
24284         dv = typeof(dv) != 'string' ? '' : dv;
24285         
24286         
24287         var keys = kv.split(',');
24288         var display = dv.split(',');
24289         for (var i = 0 ; i < keys.length; i++) {
24290             
24291             add = {};
24292             add[this.valueField] = keys[i];
24293             add[this.displayField] = display[i];
24294             this.addItem(add);
24295         }
24296       
24297         
24298     },
24299     
24300     /**
24301      * Validates the combox array value
24302      * @return {Boolean} True if the value is valid, else false
24303      */
24304     validate : function(){
24305         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24306             this.clearInvalid();
24307             return true;
24308         }
24309         return false;
24310     },
24311     
24312     validateValue : function(value){
24313         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24314         
24315     },
24316     
24317     /*@
24318      * overide
24319      * 
24320      */
24321     isDirty : function() {
24322         if(this.disabled) {
24323             return false;
24324         }
24325         
24326         try {
24327             var d = Roo.decode(String(this.originalValue));
24328         } catch (e) {
24329             return String(this.getValue()) !== String(this.originalValue);
24330         }
24331         
24332         var originalValue = [];
24333         
24334         for (var i = 0; i < d.length; i++){
24335             originalValue.push(d[i][this.valueField]);
24336         }
24337         
24338         return String(this.getValue()) !== String(originalValue.join(','));
24339         
24340     }
24341     
24342 });
24343
24344
24345
24346 /**
24347  * @class Roo.form.ComboBoxArray.Item
24348  * @extends Roo.BoxComponent
24349  * A selected item in the list
24350  *  Fred [x]  Brian [x]  [Pick another |v]
24351  * 
24352  * @constructor
24353  * Create a new item.
24354  * @param {Object} config Configuration options
24355  */
24356  
24357 Roo.form.ComboBoxArray.Item = function(config) {
24358     config.id = Roo.id();
24359     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24360 }
24361
24362 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24363     data : {},
24364     cb: false,
24365     displayField : false,
24366     tipField : false,
24367     
24368     
24369     defaultAutoCreate : {
24370         tag: 'div',
24371         cls: 'x-cbarray-item',
24372         cn : [ 
24373             { tag: 'div' },
24374             {
24375                 tag: 'img',
24376                 width:16,
24377                 height : 16,
24378                 src : Roo.BLANK_IMAGE_URL ,
24379                 align: 'center'
24380             }
24381         ]
24382         
24383     },
24384     
24385  
24386     onRender : function(ct, position)
24387     {
24388         Roo.form.Field.superclass.onRender.call(this, ct, position);
24389         
24390         if(!this.el){
24391             var cfg = this.getAutoCreate();
24392             this.el = ct.createChild(cfg, position);
24393         }
24394         
24395         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24396         
24397         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24398             this.cb.renderer(this.data) :
24399             String.format('{0}',this.data[this.displayField]);
24400         
24401             
24402         this.el.child('div').dom.setAttribute('qtip',
24403                         String.format('{0}',this.data[this.tipField])
24404         );
24405         
24406         this.el.child('img').on('click', this.remove, this);
24407         
24408     },
24409    
24410     remove : function()
24411     {
24412         if(this.cb.disabled){
24413             return;
24414         }
24415         
24416         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24417             this.cb.items.remove(this);
24418             this.el.child('img').un('click', this.remove, this);
24419             this.el.remove();
24420             this.cb.updateHiddenEl();
24421
24422             this.cb.fireEvent('remove', this.cb, this);
24423         }
24424         
24425     }
24426 });/*
24427  * Based on:
24428  * Ext JS Library 1.1.1
24429  * Copyright(c) 2006-2007, Ext JS, LLC.
24430  *
24431  * Originally Released Under LGPL - original licence link has changed is not relivant.
24432  *
24433  * Fork - LGPL
24434  * <script type="text/javascript">
24435  */
24436 /**
24437  * @class Roo.form.Checkbox
24438  * @extends Roo.form.Field
24439  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24440  * @constructor
24441  * Creates a new Checkbox
24442  * @param {Object} config Configuration options
24443  */
24444 Roo.form.Checkbox = function(config){
24445     Roo.form.Checkbox.superclass.constructor.call(this, config);
24446     this.addEvents({
24447         /**
24448          * @event check
24449          * Fires when the checkbox is checked or unchecked.
24450              * @param {Roo.form.Checkbox} this This checkbox
24451              * @param {Boolean} checked The new checked value
24452              */
24453         check : true
24454     });
24455 };
24456
24457 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24458     /**
24459      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24460      */
24461     focusClass : undefined,
24462     /**
24463      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24464      */
24465     fieldClass: "x-form-field",
24466     /**
24467      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24468      */
24469     checked: false,
24470     /**
24471      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24472      * {tag: "input", type: "checkbox", autocomplete: "off"})
24473      */
24474     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24475     /**
24476      * @cfg {String} boxLabel The text that appears beside the checkbox
24477      */
24478     boxLabel : "",
24479     /**
24480      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24481      */  
24482     inputValue : '1',
24483     /**
24484      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24485      */
24486      valueOff: '0', // value when not checked..
24487
24488     actionMode : 'viewEl', 
24489     //
24490     // private
24491     itemCls : 'x-menu-check-item x-form-item',
24492     groupClass : 'x-menu-group-item',
24493     inputType : 'hidden',
24494     
24495     
24496     inSetChecked: false, // check that we are not calling self...
24497     
24498     inputElement: false, // real input element?
24499     basedOn: false, // ????
24500     
24501     isFormField: true, // not sure where this is needed!!!!
24502
24503     onResize : function(){
24504         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24505         if(!this.boxLabel){
24506             this.el.alignTo(this.wrap, 'c-c');
24507         }
24508     },
24509
24510     initEvents : function(){
24511         Roo.form.Checkbox.superclass.initEvents.call(this);
24512         this.el.on("click", this.onClick,  this);
24513         this.el.on("change", this.onClick,  this);
24514     },
24515
24516
24517     getResizeEl : function(){
24518         return this.wrap;
24519     },
24520
24521     getPositionEl : function(){
24522         return this.wrap;
24523     },
24524
24525     // private
24526     onRender : function(ct, position){
24527         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24528         /*
24529         if(this.inputValue !== undefined){
24530             this.el.dom.value = this.inputValue;
24531         }
24532         */
24533         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24534         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24535         var viewEl = this.wrap.createChild({ 
24536             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24537         this.viewEl = viewEl;   
24538         this.wrap.on('click', this.onClick,  this); 
24539         
24540         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24541         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24542         
24543         
24544         
24545         if(this.boxLabel){
24546             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24547         //    viewEl.on('click', this.onClick,  this); 
24548         }
24549         //if(this.checked){
24550             this.setChecked(this.checked);
24551         //}else{
24552             //this.checked = this.el.dom;
24553         //}
24554
24555     },
24556
24557     // private
24558     initValue : Roo.emptyFn,
24559
24560     /**
24561      * Returns the checked state of the checkbox.
24562      * @return {Boolean} True if checked, else false
24563      */
24564     getValue : function(){
24565         if(this.el){
24566             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24567         }
24568         return this.valueOff;
24569         
24570     },
24571
24572         // private
24573     onClick : function(){ 
24574         if (this.disabled) {
24575             return;
24576         }
24577         this.setChecked(!this.checked);
24578
24579         //if(this.el.dom.checked != this.checked){
24580         //    this.setValue(this.el.dom.checked);
24581        // }
24582     },
24583
24584     /**
24585      * Sets the checked state of the checkbox.
24586      * On is always based on a string comparison between inputValue and the param.
24587      * @param {Boolean/String} value - the value to set 
24588      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24589      */
24590     setValue : function(v,suppressEvent){
24591         
24592         
24593         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24594         //if(this.el && this.el.dom){
24595         //    this.el.dom.checked = this.checked;
24596         //    this.el.dom.defaultChecked = this.checked;
24597         //}
24598         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24599         //this.fireEvent("check", this, this.checked);
24600     },
24601     // private..
24602     setChecked : function(state,suppressEvent)
24603     {
24604         if (this.inSetChecked) {
24605             this.checked = state;
24606             return;
24607         }
24608         
24609     
24610         if(this.wrap){
24611             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24612         }
24613         this.checked = state;
24614         if(suppressEvent !== true){
24615             this.fireEvent('check', this, state);
24616         }
24617         this.inSetChecked = true;
24618         this.el.dom.value = state ? this.inputValue : this.valueOff;
24619         this.inSetChecked = false;
24620         
24621     },
24622     // handle setting of hidden value by some other method!!?!?
24623     setFromHidden: function()
24624     {
24625         if(!this.el){
24626             return;
24627         }
24628         //console.log("SET FROM HIDDEN");
24629         //alert('setFrom hidden');
24630         this.setValue(this.el.dom.value);
24631     },
24632     
24633     onDestroy : function()
24634     {
24635         if(this.viewEl){
24636             Roo.get(this.viewEl).remove();
24637         }
24638          
24639         Roo.form.Checkbox.superclass.onDestroy.call(this);
24640     },
24641     
24642     setBoxLabel : function(str)
24643     {
24644         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
24645     }
24646
24647 });/*
24648  * Based on:
24649  * Ext JS Library 1.1.1
24650  * Copyright(c) 2006-2007, Ext JS, LLC.
24651  *
24652  * Originally Released Under LGPL - original licence link has changed is not relivant.
24653  *
24654  * Fork - LGPL
24655  * <script type="text/javascript">
24656  */
24657  
24658 /**
24659  * @class Roo.form.Radio
24660  * @extends Roo.form.Checkbox
24661  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24662  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24663  * @constructor
24664  * Creates a new Radio
24665  * @param {Object} config Configuration options
24666  */
24667 Roo.form.Radio = function(){
24668     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24669 };
24670 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24671     inputType: 'radio',
24672
24673     /**
24674      * If this radio is part of a group, it will return the selected value
24675      * @return {String}
24676      */
24677     getGroupValue : function(){
24678         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24679     },
24680     
24681     
24682     onRender : function(ct, position){
24683         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24684         
24685         if(this.inputValue !== undefined){
24686             this.el.dom.value = this.inputValue;
24687         }
24688          
24689         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24690         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24691         //var viewEl = this.wrap.createChild({ 
24692         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24693         //this.viewEl = viewEl;   
24694         //this.wrap.on('click', this.onClick,  this); 
24695         
24696         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24697         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24698         
24699         
24700         
24701         if(this.boxLabel){
24702             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24703         //    viewEl.on('click', this.onClick,  this); 
24704         }
24705          if(this.checked){
24706             this.el.dom.checked =   'checked' ;
24707         }
24708          
24709     } 
24710     
24711     
24712 });//<script type="text/javascript">
24713
24714 /*
24715  * Based  Ext JS Library 1.1.1
24716  * Copyright(c) 2006-2007, Ext JS, LLC.
24717  * LGPL
24718  *
24719  */
24720  
24721 /**
24722  * @class Roo.HtmlEditorCore
24723  * @extends Roo.Component
24724  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24725  *
24726  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24727  */
24728
24729 Roo.HtmlEditorCore = function(config){
24730     
24731     
24732     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24733     
24734     
24735     this.addEvents({
24736         /**
24737          * @event initialize
24738          * Fires when the editor is fully initialized (including the iframe)
24739          * @param {Roo.HtmlEditorCore} this
24740          */
24741         initialize: true,
24742         /**
24743          * @event activate
24744          * Fires when the editor is first receives the focus. Any insertion must wait
24745          * until after this event.
24746          * @param {Roo.HtmlEditorCore} this
24747          */
24748         activate: true,
24749          /**
24750          * @event beforesync
24751          * Fires before the textarea is updated with content from the editor iframe. Return false
24752          * to cancel the sync.
24753          * @param {Roo.HtmlEditorCore} this
24754          * @param {String} html
24755          */
24756         beforesync: true,
24757          /**
24758          * @event beforepush
24759          * Fires before the iframe editor is updated with content from the textarea. Return false
24760          * to cancel the push.
24761          * @param {Roo.HtmlEditorCore} this
24762          * @param {String} html
24763          */
24764         beforepush: true,
24765          /**
24766          * @event sync
24767          * Fires when the textarea is updated with content from the editor iframe.
24768          * @param {Roo.HtmlEditorCore} this
24769          * @param {String} html
24770          */
24771         sync: true,
24772          /**
24773          * @event push
24774          * Fires when the iframe editor is updated with content from the textarea.
24775          * @param {Roo.HtmlEditorCore} this
24776          * @param {String} html
24777          */
24778         push: true,
24779         
24780         /**
24781          * @event editorevent
24782          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24783          * @param {Roo.HtmlEditorCore} this
24784          */
24785         editorevent: true
24786         
24787     });
24788     
24789     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24790     
24791     // defaults : white / black...
24792     this.applyBlacklists();
24793     
24794     
24795     
24796 };
24797
24798
24799 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24800
24801
24802      /**
24803      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24804      */
24805     
24806     owner : false,
24807     
24808      /**
24809      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24810      *                        Roo.resizable.
24811      */
24812     resizable : false,
24813      /**
24814      * @cfg {Number} height (in pixels)
24815      */   
24816     height: 300,
24817    /**
24818      * @cfg {Number} width (in pixels)
24819      */   
24820     width: 500,
24821     
24822     /**
24823      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24824      * 
24825      */
24826     stylesheets: false,
24827     
24828     // id of frame..
24829     frameId: false,
24830     
24831     // private properties
24832     validationEvent : false,
24833     deferHeight: true,
24834     initialized : false,
24835     activated : false,
24836     sourceEditMode : false,
24837     onFocus : Roo.emptyFn,
24838     iframePad:3,
24839     hideMode:'offsets',
24840     
24841     clearUp: true,
24842     
24843     // blacklist + whitelisted elements..
24844     black: false,
24845     white: false,
24846      
24847     
24848
24849     /**
24850      * Protected method that will not generally be called directly. It
24851      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24852      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24853      */
24854     getDocMarkup : function(){
24855         // body styles..
24856         var st = '';
24857         
24858         // inherit styels from page...?? 
24859         if (this.stylesheets === false) {
24860             
24861             Roo.get(document.head).select('style').each(function(node) {
24862                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24863             });
24864             
24865             Roo.get(document.head).select('link').each(function(node) { 
24866                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24867             });
24868             
24869         } else if (!this.stylesheets.length) {
24870                 // simple..
24871                 st = '<style type="text/css">' +
24872                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24873                    '</style>';
24874         } else { 
24875             
24876         }
24877         
24878         st +=  '<style type="text/css">' +
24879             'IMG { cursor: pointer } ' +
24880         '</style>';
24881
24882         
24883         return '<html><head>' + st  +
24884             //<style type="text/css">' +
24885             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24886             //'</style>' +
24887             ' </head><body class="roo-htmleditor-body"></body></html>';
24888     },
24889
24890     // private
24891     onRender : function(ct, position)
24892     {
24893         var _t = this;
24894         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24895         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24896         
24897         
24898         this.el.dom.style.border = '0 none';
24899         this.el.dom.setAttribute('tabIndex', -1);
24900         this.el.addClass('x-hidden hide');
24901         
24902         
24903         
24904         if(Roo.isIE){ // fix IE 1px bogus margin
24905             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24906         }
24907        
24908         
24909         this.frameId = Roo.id();
24910         
24911          
24912         
24913         var iframe = this.owner.wrap.createChild({
24914             tag: 'iframe',
24915             cls: 'form-control', // bootstrap..
24916             id: this.frameId,
24917             name: this.frameId,
24918             frameBorder : 'no',
24919             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24920         }, this.el
24921         );
24922         
24923         
24924         this.iframe = iframe.dom;
24925
24926          this.assignDocWin();
24927         
24928         this.doc.designMode = 'on';
24929        
24930         this.doc.open();
24931         this.doc.write(this.getDocMarkup());
24932         this.doc.close();
24933
24934         
24935         var task = { // must defer to wait for browser to be ready
24936             run : function(){
24937                 //console.log("run task?" + this.doc.readyState);
24938                 this.assignDocWin();
24939                 if(this.doc.body || this.doc.readyState == 'complete'){
24940                     try {
24941                         this.doc.designMode="on";
24942                     } catch (e) {
24943                         return;
24944                     }
24945                     Roo.TaskMgr.stop(task);
24946                     this.initEditor.defer(10, this);
24947                 }
24948             },
24949             interval : 10,
24950             duration: 10000,
24951             scope: this
24952         };
24953         Roo.TaskMgr.start(task);
24954
24955     },
24956
24957     // private
24958     onResize : function(w, h)
24959     {
24960          Roo.log('resize: ' +w + ',' + h );
24961         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24962         if(!this.iframe){
24963             return;
24964         }
24965         if(typeof w == 'number'){
24966             
24967             this.iframe.style.width = w + 'px';
24968         }
24969         if(typeof h == 'number'){
24970             
24971             this.iframe.style.height = h + 'px';
24972             if(this.doc){
24973                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24974             }
24975         }
24976         
24977     },
24978
24979     /**
24980      * Toggles the editor between standard and source edit mode.
24981      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24982      */
24983     toggleSourceEdit : function(sourceEditMode){
24984         
24985         this.sourceEditMode = sourceEditMode === true;
24986         
24987         if(this.sourceEditMode){
24988  
24989             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24990             
24991         }else{
24992             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24993             //this.iframe.className = '';
24994             this.deferFocus();
24995         }
24996         //this.setSize(this.owner.wrap.getSize());
24997         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24998     },
24999
25000     
25001   
25002
25003     /**
25004      * Protected method that will not generally be called directly. If you need/want
25005      * custom HTML cleanup, this is the method you should override.
25006      * @param {String} html The HTML to be cleaned
25007      * return {String} The cleaned HTML
25008      */
25009     cleanHtml : function(html){
25010         html = String(html);
25011         if(html.length > 5){
25012             if(Roo.isSafari){ // strip safari nonsense
25013                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25014             }
25015         }
25016         if(html == '&nbsp;'){
25017             html = '';
25018         }
25019         return html;
25020     },
25021
25022     /**
25023      * HTML Editor -> Textarea
25024      * Protected method that will not generally be called directly. Syncs the contents
25025      * of the editor iframe with the textarea.
25026      */
25027     syncValue : function(){
25028         if(this.initialized){
25029             var bd = (this.doc.body || this.doc.documentElement);
25030             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25031             var html = bd.innerHTML;
25032             if(Roo.isSafari){
25033                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25034                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25035                 if(m && m[1]){
25036                     html = '<div style="'+m[0]+'">' + html + '</div>';
25037                 }
25038             }
25039             html = this.cleanHtml(html);
25040             // fix up the special chars.. normaly like back quotes in word...
25041             // however we do not want to do this with chinese..
25042             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25043                 var cc = b.charCodeAt();
25044                 if (
25045                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25046                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25047                     (cc >= 0xf900 && cc < 0xfb00 )
25048                 ) {
25049                         return b;
25050                 }
25051                 return "&#"+cc+";" 
25052             });
25053             if(this.owner.fireEvent('beforesync', this, html) !== false){
25054                 this.el.dom.value = html;
25055                 this.owner.fireEvent('sync', this, html);
25056             }
25057         }
25058     },
25059
25060     /**
25061      * Protected method that will not generally be called directly. Pushes the value of the textarea
25062      * into the iframe editor.
25063      */
25064     pushValue : function(){
25065         if(this.initialized){
25066             var v = this.el.dom.value.trim();
25067             
25068 //            if(v.length < 1){
25069 //                v = '&#160;';
25070 //            }
25071             
25072             if(this.owner.fireEvent('beforepush', this, v) !== false){
25073                 var d = (this.doc.body || this.doc.documentElement);
25074                 d.innerHTML = v;
25075                 this.cleanUpPaste();
25076                 this.el.dom.value = d.innerHTML;
25077                 this.owner.fireEvent('push', this, v);
25078             }
25079         }
25080     },
25081
25082     // private
25083     deferFocus : function(){
25084         this.focus.defer(10, this);
25085     },
25086
25087     // doc'ed in Field
25088     focus : function(){
25089         if(this.win && !this.sourceEditMode){
25090             this.win.focus();
25091         }else{
25092             this.el.focus();
25093         }
25094     },
25095     
25096     assignDocWin: function()
25097     {
25098         var iframe = this.iframe;
25099         
25100          if(Roo.isIE){
25101             this.doc = iframe.contentWindow.document;
25102             this.win = iframe.contentWindow;
25103         } else {
25104 //            if (!Roo.get(this.frameId)) {
25105 //                return;
25106 //            }
25107 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25108 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25109             
25110             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25111                 return;
25112             }
25113             
25114             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25115             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25116         }
25117     },
25118     
25119     // private
25120     initEditor : function(){
25121         //console.log("INIT EDITOR");
25122         this.assignDocWin();
25123         
25124         
25125         
25126         this.doc.designMode="on";
25127         this.doc.open();
25128         this.doc.write(this.getDocMarkup());
25129         this.doc.close();
25130         
25131         var dbody = (this.doc.body || this.doc.documentElement);
25132         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25133         // this copies styles from the containing element into thsi one..
25134         // not sure why we need all of this..
25135         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25136         
25137         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25138         //ss['background-attachment'] = 'fixed'; // w3c
25139         dbody.bgProperties = 'fixed'; // ie
25140         //Roo.DomHelper.applyStyles(dbody, ss);
25141         Roo.EventManager.on(this.doc, {
25142             //'mousedown': this.onEditorEvent,
25143             'mouseup': this.onEditorEvent,
25144             'dblclick': this.onEditorEvent,
25145             'click': this.onEditorEvent,
25146             'keyup': this.onEditorEvent,
25147             buffer:100,
25148             scope: this
25149         });
25150         if(Roo.isGecko){
25151             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25152         }
25153         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25154             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25155         }
25156         this.initialized = true;
25157
25158         this.owner.fireEvent('initialize', this);
25159         this.pushValue();
25160     },
25161
25162     // private
25163     onDestroy : function(){
25164         
25165         
25166         
25167         if(this.rendered){
25168             
25169             //for (var i =0; i < this.toolbars.length;i++) {
25170             //    // fixme - ask toolbars for heights?
25171             //    this.toolbars[i].onDestroy();
25172            // }
25173             
25174             //this.wrap.dom.innerHTML = '';
25175             //this.wrap.remove();
25176         }
25177     },
25178
25179     // private
25180     onFirstFocus : function(){
25181         
25182         this.assignDocWin();
25183         
25184         
25185         this.activated = true;
25186          
25187     
25188         if(Roo.isGecko){ // prevent silly gecko errors
25189             this.win.focus();
25190             var s = this.win.getSelection();
25191             if(!s.focusNode || s.focusNode.nodeType != 3){
25192                 var r = s.getRangeAt(0);
25193                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25194                 r.collapse(true);
25195                 this.deferFocus();
25196             }
25197             try{
25198                 this.execCmd('useCSS', true);
25199                 this.execCmd('styleWithCSS', false);
25200             }catch(e){}
25201         }
25202         this.owner.fireEvent('activate', this);
25203     },
25204
25205     // private
25206     adjustFont: function(btn){
25207         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25208         //if(Roo.isSafari){ // safari
25209         //    adjust *= 2;
25210        // }
25211         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25212         if(Roo.isSafari){ // safari
25213             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25214             v =  (v < 10) ? 10 : v;
25215             v =  (v > 48) ? 48 : v;
25216             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25217             
25218         }
25219         
25220         
25221         v = Math.max(1, v+adjust);
25222         
25223         this.execCmd('FontSize', v  );
25224     },
25225
25226     onEditorEvent : function(e)
25227     {
25228         this.owner.fireEvent('editorevent', this, e);
25229       //  this.updateToolbar();
25230         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25231     },
25232
25233     insertTag : function(tg)
25234     {
25235         // could be a bit smarter... -> wrap the current selected tRoo..
25236         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25237             
25238             range = this.createRange(this.getSelection());
25239             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25240             wrappingNode.appendChild(range.extractContents());
25241             range.insertNode(wrappingNode);
25242
25243             return;
25244             
25245             
25246             
25247         }
25248         this.execCmd("formatblock",   tg);
25249         
25250     },
25251     
25252     insertText : function(txt)
25253     {
25254         
25255         
25256         var range = this.createRange();
25257         range.deleteContents();
25258                //alert(Sender.getAttribute('label'));
25259                
25260         range.insertNode(this.doc.createTextNode(txt));
25261     } ,
25262     
25263      
25264
25265     /**
25266      * Executes a Midas editor command on the editor document and performs necessary focus and
25267      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25268      * @param {String} cmd The Midas command
25269      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25270      */
25271     relayCmd : function(cmd, value){
25272         this.win.focus();
25273         this.execCmd(cmd, value);
25274         this.owner.fireEvent('editorevent', this);
25275         //this.updateToolbar();
25276         this.owner.deferFocus();
25277     },
25278
25279     /**
25280      * Executes a Midas editor command directly on the editor document.
25281      * For visual commands, you should use {@link #relayCmd} instead.
25282      * <b>This should only be called after the editor is initialized.</b>
25283      * @param {String} cmd The Midas command
25284      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25285      */
25286     execCmd : function(cmd, value){
25287         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25288         this.syncValue();
25289     },
25290  
25291  
25292    
25293     /**
25294      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25295      * to insert tRoo.
25296      * @param {String} text | dom node.. 
25297      */
25298     insertAtCursor : function(text)
25299     {
25300         
25301         
25302         
25303         if(!this.activated){
25304             return;
25305         }
25306         /*
25307         if(Roo.isIE){
25308             this.win.focus();
25309             var r = this.doc.selection.createRange();
25310             if(r){
25311                 r.collapse(true);
25312                 r.pasteHTML(text);
25313                 this.syncValue();
25314                 this.deferFocus();
25315             
25316             }
25317             return;
25318         }
25319         */
25320         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25321             this.win.focus();
25322             
25323             
25324             // from jquery ui (MIT licenced)
25325             var range, node;
25326             var win = this.win;
25327             
25328             if (win.getSelection && win.getSelection().getRangeAt) {
25329                 range = win.getSelection().getRangeAt(0);
25330                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25331                 range.insertNode(node);
25332             } else if (win.document.selection && win.document.selection.createRange) {
25333                 // no firefox support
25334                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25335                 win.document.selection.createRange().pasteHTML(txt);
25336             } else {
25337                 // no firefox support
25338                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25339                 this.execCmd('InsertHTML', txt);
25340             } 
25341             
25342             this.syncValue();
25343             
25344             this.deferFocus();
25345         }
25346     },
25347  // private
25348     mozKeyPress : function(e){
25349         if(e.ctrlKey){
25350             var c = e.getCharCode(), cmd;
25351           
25352             if(c > 0){
25353                 c = String.fromCharCode(c).toLowerCase();
25354                 switch(c){
25355                     case 'b':
25356                         cmd = 'bold';
25357                         break;
25358                     case 'i':
25359                         cmd = 'italic';
25360                         break;
25361                     
25362                     case 'u':
25363                         cmd = 'underline';
25364                         break;
25365                     
25366                     case 'v':
25367                         this.cleanUpPaste.defer(100, this);
25368                         return;
25369                         
25370                 }
25371                 if(cmd){
25372                     this.win.focus();
25373                     this.execCmd(cmd);
25374                     this.deferFocus();
25375                     e.preventDefault();
25376                 }
25377                 
25378             }
25379         }
25380     },
25381
25382     // private
25383     fixKeys : function(){ // load time branching for fastest keydown performance
25384         if(Roo.isIE){
25385             return function(e){
25386                 var k = e.getKey(), r;
25387                 if(k == e.TAB){
25388                     e.stopEvent();
25389                     r = this.doc.selection.createRange();
25390                     if(r){
25391                         r.collapse(true);
25392                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25393                         this.deferFocus();
25394                     }
25395                     return;
25396                 }
25397                 
25398                 if(k == e.ENTER){
25399                     r = this.doc.selection.createRange();
25400                     if(r){
25401                         var target = r.parentElement();
25402                         if(!target || target.tagName.toLowerCase() != 'li'){
25403                             e.stopEvent();
25404                             r.pasteHTML('<br />');
25405                             r.collapse(false);
25406                             r.select();
25407                         }
25408                     }
25409                 }
25410                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25411                     this.cleanUpPaste.defer(100, this);
25412                     return;
25413                 }
25414                 
25415                 
25416             };
25417         }else if(Roo.isOpera){
25418             return function(e){
25419                 var k = e.getKey();
25420                 if(k == e.TAB){
25421                     e.stopEvent();
25422                     this.win.focus();
25423                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25424                     this.deferFocus();
25425                 }
25426                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25427                     this.cleanUpPaste.defer(100, this);
25428                     return;
25429                 }
25430                 
25431             };
25432         }else if(Roo.isSafari){
25433             return function(e){
25434                 var k = e.getKey();
25435                 
25436                 if(k == e.TAB){
25437                     e.stopEvent();
25438                     this.execCmd('InsertText','\t');
25439                     this.deferFocus();
25440                     return;
25441                 }
25442                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25443                     this.cleanUpPaste.defer(100, this);
25444                     return;
25445                 }
25446                 
25447              };
25448         }
25449     }(),
25450     
25451     getAllAncestors: function()
25452     {
25453         var p = this.getSelectedNode();
25454         var a = [];
25455         if (!p) {
25456             a.push(p); // push blank onto stack..
25457             p = this.getParentElement();
25458         }
25459         
25460         
25461         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25462             a.push(p);
25463             p = p.parentNode;
25464         }
25465         a.push(this.doc.body);
25466         return a;
25467     },
25468     lastSel : false,
25469     lastSelNode : false,
25470     
25471     
25472     getSelection : function() 
25473     {
25474         this.assignDocWin();
25475         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25476     },
25477     
25478     getSelectedNode: function() 
25479     {
25480         // this may only work on Gecko!!!
25481         
25482         // should we cache this!!!!
25483         
25484         
25485         
25486          
25487         var range = this.createRange(this.getSelection()).cloneRange();
25488         
25489         if (Roo.isIE) {
25490             var parent = range.parentElement();
25491             while (true) {
25492                 var testRange = range.duplicate();
25493                 testRange.moveToElementText(parent);
25494                 if (testRange.inRange(range)) {
25495                     break;
25496                 }
25497                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25498                     break;
25499                 }
25500                 parent = parent.parentElement;
25501             }
25502             return parent;
25503         }
25504         
25505         // is ancestor a text element.
25506         var ac =  range.commonAncestorContainer;
25507         if (ac.nodeType == 3) {
25508             ac = ac.parentNode;
25509         }
25510         
25511         var ar = ac.childNodes;
25512          
25513         var nodes = [];
25514         var other_nodes = [];
25515         var has_other_nodes = false;
25516         for (var i=0;i<ar.length;i++) {
25517             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25518                 continue;
25519             }
25520             // fullly contained node.
25521             
25522             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25523                 nodes.push(ar[i]);
25524                 continue;
25525             }
25526             
25527             // probably selected..
25528             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25529                 other_nodes.push(ar[i]);
25530                 continue;
25531             }
25532             // outer..
25533             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25534                 continue;
25535             }
25536             
25537             
25538             has_other_nodes = true;
25539         }
25540         if (!nodes.length && other_nodes.length) {
25541             nodes= other_nodes;
25542         }
25543         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25544             return false;
25545         }
25546         
25547         return nodes[0];
25548     },
25549     createRange: function(sel)
25550     {
25551         // this has strange effects when using with 
25552         // top toolbar - not sure if it's a great idea.
25553         //this.editor.contentWindow.focus();
25554         if (typeof sel != "undefined") {
25555             try {
25556                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25557             } catch(e) {
25558                 return this.doc.createRange();
25559             }
25560         } else {
25561             return this.doc.createRange();
25562         }
25563     },
25564     getParentElement: function()
25565     {
25566         
25567         this.assignDocWin();
25568         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25569         
25570         var range = this.createRange(sel);
25571          
25572         try {
25573             var p = range.commonAncestorContainer;
25574             while (p.nodeType == 3) { // text node
25575                 p = p.parentNode;
25576             }
25577             return p;
25578         } catch (e) {
25579             return null;
25580         }
25581     
25582     },
25583     /***
25584      *
25585      * Range intersection.. the hard stuff...
25586      *  '-1' = before
25587      *  '0' = hits..
25588      *  '1' = after.
25589      *         [ -- selected range --- ]
25590      *   [fail]                        [fail]
25591      *
25592      *    basically..
25593      *      if end is before start or  hits it. fail.
25594      *      if start is after end or hits it fail.
25595      *
25596      *   if either hits (but other is outside. - then it's not 
25597      *   
25598      *    
25599      **/
25600     
25601     
25602     // @see http://www.thismuchiknow.co.uk/?p=64.
25603     rangeIntersectsNode : function(range, node)
25604     {
25605         var nodeRange = node.ownerDocument.createRange();
25606         try {
25607             nodeRange.selectNode(node);
25608         } catch (e) {
25609             nodeRange.selectNodeContents(node);
25610         }
25611     
25612         var rangeStartRange = range.cloneRange();
25613         rangeStartRange.collapse(true);
25614     
25615         var rangeEndRange = range.cloneRange();
25616         rangeEndRange.collapse(false);
25617     
25618         var nodeStartRange = nodeRange.cloneRange();
25619         nodeStartRange.collapse(true);
25620     
25621         var nodeEndRange = nodeRange.cloneRange();
25622         nodeEndRange.collapse(false);
25623     
25624         return rangeStartRange.compareBoundaryPoints(
25625                  Range.START_TO_START, nodeEndRange) == -1 &&
25626                rangeEndRange.compareBoundaryPoints(
25627                  Range.START_TO_START, nodeStartRange) == 1;
25628         
25629          
25630     },
25631     rangeCompareNode : function(range, node)
25632     {
25633         var nodeRange = node.ownerDocument.createRange();
25634         try {
25635             nodeRange.selectNode(node);
25636         } catch (e) {
25637             nodeRange.selectNodeContents(node);
25638         }
25639         
25640         
25641         range.collapse(true);
25642     
25643         nodeRange.collapse(true);
25644      
25645         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25646         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25647          
25648         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25649         
25650         var nodeIsBefore   =  ss == 1;
25651         var nodeIsAfter    = ee == -1;
25652         
25653         if (nodeIsBefore && nodeIsAfter) {
25654             return 0; // outer
25655         }
25656         if (!nodeIsBefore && nodeIsAfter) {
25657             return 1; //right trailed.
25658         }
25659         
25660         if (nodeIsBefore && !nodeIsAfter) {
25661             return 2;  // left trailed.
25662         }
25663         // fully contined.
25664         return 3;
25665     },
25666
25667     // private? - in a new class?
25668     cleanUpPaste :  function()
25669     {
25670         // cleans up the whole document..
25671         Roo.log('cleanuppaste');
25672         
25673         this.cleanUpChildren(this.doc.body);
25674         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25675         if (clean != this.doc.body.innerHTML) {
25676             this.doc.body.innerHTML = clean;
25677         }
25678         
25679     },
25680     
25681     cleanWordChars : function(input) {// change the chars to hex code
25682         var he = Roo.HtmlEditorCore;
25683         
25684         var output = input;
25685         Roo.each(he.swapCodes, function(sw) { 
25686             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25687             
25688             output = output.replace(swapper, sw[1]);
25689         });
25690         
25691         return output;
25692     },
25693     
25694     
25695     cleanUpChildren : function (n)
25696     {
25697         if (!n.childNodes.length) {
25698             return;
25699         }
25700         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25701            this.cleanUpChild(n.childNodes[i]);
25702         }
25703     },
25704     
25705     
25706         
25707     
25708     cleanUpChild : function (node)
25709     {
25710         var ed = this;
25711         //console.log(node);
25712         if (node.nodeName == "#text") {
25713             // clean up silly Windows -- stuff?
25714             return; 
25715         }
25716         if (node.nodeName == "#comment") {
25717             node.parentNode.removeChild(node);
25718             // clean up silly Windows -- stuff?
25719             return; 
25720         }
25721         var lcname = node.tagName.toLowerCase();
25722         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25723         // whitelist of tags..
25724         
25725         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25726             // remove node.
25727             node.parentNode.removeChild(node);
25728             return;
25729             
25730         }
25731         
25732         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25733         
25734         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25735         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25736         
25737         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25738         //    remove_keep_children = true;
25739         //}
25740         
25741         if (remove_keep_children) {
25742             this.cleanUpChildren(node);
25743             // inserts everything just before this node...
25744             while (node.childNodes.length) {
25745                 var cn = node.childNodes[0];
25746                 node.removeChild(cn);
25747                 node.parentNode.insertBefore(cn, node);
25748             }
25749             node.parentNode.removeChild(node);
25750             return;
25751         }
25752         
25753         if (!node.attributes || !node.attributes.length) {
25754             this.cleanUpChildren(node);
25755             return;
25756         }
25757         
25758         function cleanAttr(n,v)
25759         {
25760             
25761             if (v.match(/^\./) || v.match(/^\//)) {
25762                 return;
25763             }
25764             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25765                 return;
25766             }
25767             if (v.match(/^#/)) {
25768                 return;
25769             }
25770 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25771             node.removeAttribute(n);
25772             
25773         }
25774         
25775         var cwhite = this.cwhite;
25776         var cblack = this.cblack;
25777             
25778         function cleanStyle(n,v)
25779         {
25780             if (v.match(/expression/)) { //XSS?? should we even bother..
25781                 node.removeAttribute(n);
25782                 return;
25783             }
25784             
25785             var parts = v.split(/;/);
25786             var clean = [];
25787             
25788             Roo.each(parts, function(p) {
25789                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25790                 if (!p.length) {
25791                     return true;
25792                 }
25793                 var l = p.split(':').shift().replace(/\s+/g,'');
25794                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25795                 
25796                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25797 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25798                     //node.removeAttribute(n);
25799                     return true;
25800                 }
25801                 //Roo.log()
25802                 // only allow 'c whitelisted system attributes'
25803                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25804 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25805                     //node.removeAttribute(n);
25806                     return true;
25807                 }
25808                 
25809                 
25810                  
25811                 
25812                 clean.push(p);
25813                 return true;
25814             });
25815             if (clean.length) { 
25816                 node.setAttribute(n, clean.join(';'));
25817             } else {
25818                 node.removeAttribute(n);
25819             }
25820             
25821         }
25822         
25823         
25824         for (var i = node.attributes.length-1; i > -1 ; i--) {
25825             var a = node.attributes[i];
25826             //console.log(a);
25827             
25828             if (a.name.toLowerCase().substr(0,2)=='on')  {
25829                 node.removeAttribute(a.name);
25830                 continue;
25831             }
25832             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25833                 node.removeAttribute(a.name);
25834                 continue;
25835             }
25836             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25837                 cleanAttr(a.name,a.value); // fixme..
25838                 continue;
25839             }
25840             if (a.name == 'style') {
25841                 cleanStyle(a.name,a.value);
25842                 continue;
25843             }
25844             /// clean up MS crap..
25845             // tecnically this should be a list of valid class'es..
25846             
25847             
25848             if (a.name == 'class') {
25849                 if (a.value.match(/^Mso/)) {
25850                     node.className = '';
25851                 }
25852                 
25853                 if (a.value.match(/body/)) {
25854                     node.className = '';
25855                 }
25856                 continue;
25857             }
25858             
25859             // style cleanup!?
25860             // class cleanup?
25861             
25862         }
25863         
25864         
25865         this.cleanUpChildren(node);
25866         
25867         
25868     },
25869     
25870     /**
25871      * Clean up MS wordisms...
25872      */
25873     cleanWord : function(node)
25874     {
25875         
25876         
25877         if (!node) {
25878             this.cleanWord(this.doc.body);
25879             return;
25880         }
25881         if (node.nodeName == "#text") {
25882             // clean up silly Windows -- stuff?
25883             return; 
25884         }
25885         if (node.nodeName == "#comment") {
25886             node.parentNode.removeChild(node);
25887             // clean up silly Windows -- stuff?
25888             return; 
25889         }
25890         
25891         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25892             node.parentNode.removeChild(node);
25893             return;
25894         }
25895         
25896         // remove - but keep children..
25897         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25898             while (node.childNodes.length) {
25899                 var cn = node.childNodes[0];
25900                 node.removeChild(cn);
25901                 node.parentNode.insertBefore(cn, node);
25902             }
25903             node.parentNode.removeChild(node);
25904             this.iterateChildren(node, this.cleanWord);
25905             return;
25906         }
25907         // clean styles
25908         if (node.className.length) {
25909             
25910             var cn = node.className.split(/\W+/);
25911             var cna = [];
25912             Roo.each(cn, function(cls) {
25913                 if (cls.match(/Mso[a-zA-Z]+/)) {
25914                     return;
25915                 }
25916                 cna.push(cls);
25917             });
25918             node.className = cna.length ? cna.join(' ') : '';
25919             if (!cna.length) {
25920                 node.removeAttribute("class");
25921             }
25922         }
25923         
25924         if (node.hasAttribute("lang")) {
25925             node.removeAttribute("lang");
25926         }
25927         
25928         if (node.hasAttribute("style")) {
25929             
25930             var styles = node.getAttribute("style").split(";");
25931             var nstyle = [];
25932             Roo.each(styles, function(s) {
25933                 if (!s.match(/:/)) {
25934                     return;
25935                 }
25936                 var kv = s.split(":");
25937                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25938                     return;
25939                 }
25940                 // what ever is left... we allow.
25941                 nstyle.push(s);
25942             });
25943             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25944             if (!nstyle.length) {
25945                 node.removeAttribute('style');
25946             }
25947         }
25948         this.iterateChildren(node, this.cleanWord);
25949         
25950         
25951         
25952     },
25953     /**
25954      * iterateChildren of a Node, calling fn each time, using this as the scole..
25955      * @param {DomNode} node node to iterate children of.
25956      * @param {Function} fn method of this class to call on each item.
25957      */
25958     iterateChildren : function(node, fn)
25959     {
25960         if (!node.childNodes.length) {
25961                 return;
25962         }
25963         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25964            fn.call(this, node.childNodes[i])
25965         }
25966     },
25967     
25968     
25969     /**
25970      * cleanTableWidths.
25971      *
25972      * Quite often pasting from word etc.. results in tables with column and widths.
25973      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25974      *
25975      */
25976     cleanTableWidths : function(node)
25977     {
25978          
25979          
25980         if (!node) {
25981             this.cleanTableWidths(this.doc.body);
25982             return;
25983         }
25984         
25985         // ignore list...
25986         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25987             return; 
25988         }
25989         Roo.log(node.tagName);
25990         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25991             this.iterateChildren(node, this.cleanTableWidths);
25992             return;
25993         }
25994         if (node.hasAttribute('width')) {
25995             node.removeAttribute('width');
25996         }
25997         
25998          
25999         if (node.hasAttribute("style")) {
26000             // pretty basic...
26001             
26002             var styles = node.getAttribute("style").split(";");
26003             var nstyle = [];
26004             Roo.each(styles, function(s) {
26005                 if (!s.match(/:/)) {
26006                     return;
26007                 }
26008                 var kv = s.split(":");
26009                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26010                     return;
26011                 }
26012                 // what ever is left... we allow.
26013                 nstyle.push(s);
26014             });
26015             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26016             if (!nstyle.length) {
26017                 node.removeAttribute('style');
26018             }
26019         }
26020         
26021         this.iterateChildren(node, this.cleanTableWidths);
26022         
26023         
26024     },
26025     
26026     
26027     
26028     
26029     domToHTML : function(currentElement, depth, nopadtext) {
26030         
26031         depth = depth || 0;
26032         nopadtext = nopadtext || false;
26033     
26034         if (!currentElement) {
26035             return this.domToHTML(this.doc.body);
26036         }
26037         
26038         //Roo.log(currentElement);
26039         var j;
26040         var allText = false;
26041         var nodeName = currentElement.nodeName;
26042         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26043         
26044         if  (nodeName == '#text') {
26045             
26046             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26047         }
26048         
26049         
26050         var ret = '';
26051         if (nodeName != 'BODY') {
26052              
26053             var i = 0;
26054             // Prints the node tagName, such as <A>, <IMG>, etc
26055             if (tagName) {
26056                 var attr = [];
26057                 for(i = 0; i < currentElement.attributes.length;i++) {
26058                     // quoting?
26059                     var aname = currentElement.attributes.item(i).name;
26060                     if (!currentElement.attributes.item(i).value.length) {
26061                         continue;
26062                     }
26063                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26064                 }
26065                 
26066                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26067             } 
26068             else {
26069                 
26070                 // eack
26071             }
26072         } else {
26073             tagName = false;
26074         }
26075         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26076             return ret;
26077         }
26078         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26079             nopadtext = true;
26080         }
26081         
26082         
26083         // Traverse the tree
26084         i = 0;
26085         var currentElementChild = currentElement.childNodes.item(i);
26086         var allText = true;
26087         var innerHTML  = '';
26088         lastnode = '';
26089         while (currentElementChild) {
26090             // Formatting code (indent the tree so it looks nice on the screen)
26091             var nopad = nopadtext;
26092             if (lastnode == 'SPAN') {
26093                 nopad  = true;
26094             }
26095             // text
26096             if  (currentElementChild.nodeName == '#text') {
26097                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26098                 toadd = nopadtext ? toadd : toadd.trim();
26099                 if (!nopad && toadd.length > 80) {
26100                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26101                 }
26102                 innerHTML  += toadd;
26103                 
26104                 i++;
26105                 currentElementChild = currentElement.childNodes.item(i);
26106                 lastNode = '';
26107                 continue;
26108             }
26109             allText = false;
26110             
26111             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26112                 
26113             // Recursively traverse the tree structure of the child node
26114             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26115             lastnode = currentElementChild.nodeName;
26116             i++;
26117             currentElementChild=currentElement.childNodes.item(i);
26118         }
26119         
26120         ret += innerHTML;
26121         
26122         if (!allText) {
26123                 // The remaining code is mostly for formatting the tree
26124             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26125         }
26126         
26127         
26128         if (tagName) {
26129             ret+= "</"+tagName+">";
26130         }
26131         return ret;
26132         
26133     },
26134         
26135     applyBlacklists : function()
26136     {
26137         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26138         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26139         
26140         this.white = [];
26141         this.black = [];
26142         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26143             if (b.indexOf(tag) > -1) {
26144                 return;
26145             }
26146             this.white.push(tag);
26147             
26148         }, this);
26149         
26150         Roo.each(w, function(tag) {
26151             if (b.indexOf(tag) > -1) {
26152                 return;
26153             }
26154             if (this.white.indexOf(tag) > -1) {
26155                 return;
26156             }
26157             this.white.push(tag);
26158             
26159         }, this);
26160         
26161         
26162         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26163             if (w.indexOf(tag) > -1) {
26164                 return;
26165             }
26166             this.black.push(tag);
26167             
26168         }, this);
26169         
26170         Roo.each(b, function(tag) {
26171             if (w.indexOf(tag) > -1) {
26172                 return;
26173             }
26174             if (this.black.indexOf(tag) > -1) {
26175                 return;
26176             }
26177             this.black.push(tag);
26178             
26179         }, this);
26180         
26181         
26182         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26183         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26184         
26185         this.cwhite = [];
26186         this.cblack = [];
26187         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26188             if (b.indexOf(tag) > -1) {
26189                 return;
26190             }
26191             this.cwhite.push(tag);
26192             
26193         }, this);
26194         
26195         Roo.each(w, function(tag) {
26196             if (b.indexOf(tag) > -1) {
26197                 return;
26198             }
26199             if (this.cwhite.indexOf(tag) > -1) {
26200                 return;
26201             }
26202             this.cwhite.push(tag);
26203             
26204         }, this);
26205         
26206         
26207         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26208             if (w.indexOf(tag) > -1) {
26209                 return;
26210             }
26211             this.cblack.push(tag);
26212             
26213         }, this);
26214         
26215         Roo.each(b, function(tag) {
26216             if (w.indexOf(tag) > -1) {
26217                 return;
26218             }
26219             if (this.cblack.indexOf(tag) > -1) {
26220                 return;
26221             }
26222             this.cblack.push(tag);
26223             
26224         }, this);
26225     },
26226     
26227     setStylesheets : function(stylesheets)
26228     {
26229         if(typeof(stylesheets) == 'string'){
26230             Roo.get(this.iframe.contentDocument.head).createChild({
26231                 tag : 'link',
26232                 rel : 'stylesheet',
26233                 type : 'text/css',
26234                 href : stylesheets
26235             });
26236             
26237             return;
26238         }
26239         var _this = this;
26240      
26241         Roo.each(stylesheets, function(s) {
26242             if(!s.length){
26243                 return;
26244             }
26245             
26246             Roo.get(_this.iframe.contentDocument.head).createChild({
26247                 tag : 'link',
26248                 rel : 'stylesheet',
26249                 type : 'text/css',
26250                 href : s
26251             });
26252         });
26253
26254         
26255     },
26256     
26257     removeStylesheets : function()
26258     {
26259         var _this = this;
26260         
26261         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26262             s.remove();
26263         });
26264     }
26265     
26266     // hide stuff that is not compatible
26267     /**
26268      * @event blur
26269      * @hide
26270      */
26271     /**
26272      * @event change
26273      * @hide
26274      */
26275     /**
26276      * @event focus
26277      * @hide
26278      */
26279     /**
26280      * @event specialkey
26281      * @hide
26282      */
26283     /**
26284      * @cfg {String} fieldClass @hide
26285      */
26286     /**
26287      * @cfg {String} focusClass @hide
26288      */
26289     /**
26290      * @cfg {String} autoCreate @hide
26291      */
26292     /**
26293      * @cfg {String} inputType @hide
26294      */
26295     /**
26296      * @cfg {String} invalidClass @hide
26297      */
26298     /**
26299      * @cfg {String} invalidText @hide
26300      */
26301     /**
26302      * @cfg {String} msgFx @hide
26303      */
26304     /**
26305      * @cfg {String} validateOnBlur @hide
26306      */
26307 });
26308
26309 Roo.HtmlEditorCore.white = [
26310         'area', 'br', 'img', 'input', 'hr', 'wbr',
26311         
26312        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26313        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26314        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26315        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26316        'table',   'ul',         'xmp', 
26317        
26318        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26319       'thead',   'tr', 
26320      
26321       'dir', 'menu', 'ol', 'ul', 'dl',
26322        
26323       'embed',  'object'
26324 ];
26325
26326
26327 Roo.HtmlEditorCore.black = [
26328     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26329         'applet', // 
26330         'base',   'basefont', 'bgsound', 'blink',  'body', 
26331         'frame',  'frameset', 'head',    'html',   'ilayer', 
26332         'iframe', 'layer',  'link',     'meta',    'object',   
26333         'script', 'style' ,'title',  'xml' // clean later..
26334 ];
26335 Roo.HtmlEditorCore.clean = [
26336     'script', 'style', 'title', 'xml'
26337 ];
26338 Roo.HtmlEditorCore.remove = [
26339     'font'
26340 ];
26341 // attributes..
26342
26343 Roo.HtmlEditorCore.ablack = [
26344     'on'
26345 ];
26346     
26347 Roo.HtmlEditorCore.aclean = [ 
26348     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26349 ];
26350
26351 // protocols..
26352 Roo.HtmlEditorCore.pwhite= [
26353         'http',  'https',  'mailto'
26354 ];
26355
26356 // white listed style attributes.
26357 Roo.HtmlEditorCore.cwhite= [
26358       //  'text-align', /// default is to allow most things..
26359       
26360          
26361 //        'font-size'//??
26362 ];
26363
26364 // black listed style attributes.
26365 Roo.HtmlEditorCore.cblack= [
26366       //  'font-size' -- this can be set by the project 
26367 ];
26368
26369
26370 Roo.HtmlEditorCore.swapCodes   =[ 
26371     [    8211, "--" ], 
26372     [    8212, "--" ], 
26373     [    8216,  "'" ],  
26374     [    8217, "'" ],  
26375     [    8220, '"' ],  
26376     [    8221, '"' ],  
26377     [    8226, "*" ],  
26378     [    8230, "..." ]
26379 ]; 
26380
26381     //<script type="text/javascript">
26382
26383 /*
26384  * Ext JS Library 1.1.1
26385  * Copyright(c) 2006-2007, Ext JS, LLC.
26386  * Licence LGPL
26387  * 
26388  */
26389  
26390  
26391 Roo.form.HtmlEditor = function(config){
26392     
26393     
26394     
26395     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26396     
26397     if (!this.toolbars) {
26398         this.toolbars = [];
26399     }
26400     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26401     
26402     
26403 };
26404
26405 /**
26406  * @class Roo.form.HtmlEditor
26407  * @extends Roo.form.Field
26408  * Provides a lightweight HTML Editor component.
26409  *
26410  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26411  * 
26412  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26413  * supported by this editor.</b><br/><br/>
26414  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26415  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26416  */
26417 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26418     /**
26419      * @cfg {Boolean} clearUp
26420      */
26421     clearUp : true,
26422       /**
26423      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26424      */
26425     toolbars : false,
26426    
26427      /**
26428      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26429      *                        Roo.resizable.
26430      */
26431     resizable : false,
26432      /**
26433      * @cfg {Number} height (in pixels)
26434      */   
26435     height: 300,
26436    /**
26437      * @cfg {Number} width (in pixels)
26438      */   
26439     width: 500,
26440     
26441     /**
26442      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26443      * 
26444      */
26445     stylesheets: false,
26446     
26447     
26448      /**
26449      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26450      * 
26451      */
26452     cblack: false,
26453     /**
26454      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26455      * 
26456      */
26457     cwhite: false,
26458     
26459      /**
26460      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26461      * 
26462      */
26463     black: false,
26464     /**
26465      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26466      * 
26467      */
26468     white: false,
26469     
26470     // id of frame..
26471     frameId: false,
26472     
26473     // private properties
26474     validationEvent : false,
26475     deferHeight: true,
26476     initialized : false,
26477     activated : false,
26478     
26479     onFocus : Roo.emptyFn,
26480     iframePad:3,
26481     hideMode:'offsets',
26482     
26483     actionMode : 'container', // defaults to hiding it...
26484     
26485     defaultAutoCreate : { // modified by initCompnoent..
26486         tag: "textarea",
26487         style:"width:500px;height:300px;",
26488         autocomplete: "new-password"
26489     },
26490
26491     // private
26492     initComponent : function(){
26493         this.addEvents({
26494             /**
26495              * @event initialize
26496              * Fires when the editor is fully initialized (including the iframe)
26497              * @param {HtmlEditor} this
26498              */
26499             initialize: true,
26500             /**
26501              * @event activate
26502              * Fires when the editor is first receives the focus. Any insertion must wait
26503              * until after this event.
26504              * @param {HtmlEditor} this
26505              */
26506             activate: true,
26507              /**
26508              * @event beforesync
26509              * Fires before the textarea is updated with content from the editor iframe. Return false
26510              * to cancel the sync.
26511              * @param {HtmlEditor} this
26512              * @param {String} html
26513              */
26514             beforesync: true,
26515              /**
26516              * @event beforepush
26517              * Fires before the iframe editor is updated with content from the textarea. Return false
26518              * to cancel the push.
26519              * @param {HtmlEditor} this
26520              * @param {String} html
26521              */
26522             beforepush: true,
26523              /**
26524              * @event sync
26525              * Fires when the textarea is updated with content from the editor iframe.
26526              * @param {HtmlEditor} this
26527              * @param {String} html
26528              */
26529             sync: true,
26530              /**
26531              * @event push
26532              * Fires when the iframe editor is updated with content from the textarea.
26533              * @param {HtmlEditor} this
26534              * @param {String} html
26535              */
26536             push: true,
26537              /**
26538              * @event editmodechange
26539              * Fires when the editor switches edit modes
26540              * @param {HtmlEditor} this
26541              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26542              */
26543             editmodechange: true,
26544             /**
26545              * @event editorevent
26546              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26547              * @param {HtmlEditor} this
26548              */
26549             editorevent: true,
26550             /**
26551              * @event firstfocus
26552              * Fires when on first focus - needed by toolbars..
26553              * @param {HtmlEditor} this
26554              */
26555             firstfocus: true,
26556             /**
26557              * @event autosave
26558              * Auto save the htmlEditor value as a file into Events
26559              * @param {HtmlEditor} this
26560              */
26561             autosave: true,
26562             /**
26563              * @event savedpreview
26564              * preview the saved version of htmlEditor
26565              * @param {HtmlEditor} this
26566              */
26567             savedpreview: true,
26568             
26569             /**
26570             * @event stylesheetsclick
26571             * Fires when press the Sytlesheets button
26572             * @param {Roo.HtmlEditorCore} this
26573             */
26574             stylesheetsclick: true
26575         });
26576         this.defaultAutoCreate =  {
26577             tag: "textarea",
26578             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26579             autocomplete: "new-password"
26580         };
26581     },
26582
26583     /**
26584      * Protected method that will not generally be called directly. It
26585      * is called when the editor creates its toolbar. Override this method if you need to
26586      * add custom toolbar buttons.
26587      * @param {HtmlEditor} editor
26588      */
26589     createToolbar : function(editor){
26590         Roo.log("create toolbars");
26591         if (!editor.toolbars || !editor.toolbars.length) {
26592             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26593         }
26594         
26595         for (var i =0 ; i < editor.toolbars.length;i++) {
26596             editor.toolbars[i] = Roo.factory(
26597                     typeof(editor.toolbars[i]) == 'string' ?
26598                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26599                 Roo.form.HtmlEditor);
26600             editor.toolbars[i].init(editor);
26601         }
26602          
26603         
26604     },
26605
26606      
26607     // private
26608     onRender : function(ct, position)
26609     {
26610         var _t = this;
26611         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26612         
26613         this.wrap = this.el.wrap({
26614             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26615         });
26616         
26617         this.editorcore.onRender(ct, position);
26618          
26619         if (this.resizable) {
26620             this.resizeEl = new Roo.Resizable(this.wrap, {
26621                 pinned : true,
26622                 wrap: true,
26623                 dynamic : true,
26624                 minHeight : this.height,
26625                 height: this.height,
26626                 handles : this.resizable,
26627                 width: this.width,
26628                 listeners : {
26629                     resize : function(r, w, h) {
26630                         _t.onResize(w,h); // -something
26631                     }
26632                 }
26633             });
26634             
26635         }
26636         this.createToolbar(this);
26637        
26638         
26639         if(!this.width){
26640             this.setSize(this.wrap.getSize());
26641         }
26642         if (this.resizeEl) {
26643             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26644             // should trigger onReize..
26645         }
26646         
26647         this.keyNav = new Roo.KeyNav(this.el, {
26648             
26649             "tab" : function(e){
26650                 e.preventDefault();
26651                 
26652                 var value = this.getValue();
26653                 
26654                 var start = this.el.dom.selectionStart;
26655                 var end = this.el.dom.selectionEnd;
26656                 
26657                 if(!e.shiftKey){
26658                     
26659                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26660                     this.el.dom.setSelectionRange(end + 1, end + 1);
26661                     return;
26662                 }
26663                 
26664                 var f = value.substring(0, start).split("\t");
26665                 
26666                 if(f.pop().length != 0){
26667                     return;
26668                 }
26669                 
26670                 this.setValue(f.join("\t") + value.substring(end));
26671                 this.el.dom.setSelectionRange(start - 1, start - 1);
26672                 
26673             },
26674             
26675             "home" : function(e){
26676                 e.preventDefault();
26677                 
26678                 var curr = this.el.dom.selectionStart;
26679                 var lines = this.getValue().split("\n");
26680                 
26681                 if(!lines.length){
26682                     return;
26683                 }
26684                 
26685                 if(e.ctrlKey){
26686                     this.el.dom.setSelectionRange(0, 0);
26687                     return;
26688                 }
26689                 
26690                 var pos = 0;
26691                 
26692                 for (var i = 0; i < lines.length;i++) {
26693                     pos += lines[i].length;
26694                     
26695                     if(i != 0){
26696                         pos += 1;
26697                     }
26698                     
26699                     if(pos < curr){
26700                         continue;
26701                     }
26702                     
26703                     pos -= lines[i].length;
26704                     
26705                     break;
26706                 }
26707                 
26708                 if(!e.shiftKey){
26709                     this.el.dom.setSelectionRange(pos, pos);
26710                     return;
26711                 }
26712                 
26713                 this.el.dom.selectionStart = pos;
26714                 this.el.dom.selectionEnd = curr;
26715             },
26716             
26717             "end" : function(e){
26718                 e.preventDefault();
26719                 
26720                 var curr = this.el.dom.selectionStart;
26721                 var lines = this.getValue().split("\n");
26722                 
26723                 if(!lines.length){
26724                     return;
26725                 }
26726                 
26727                 if(e.ctrlKey){
26728                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26729                     return;
26730                 }
26731                 
26732                 var pos = 0;
26733                 
26734                 for (var i = 0; i < lines.length;i++) {
26735                     
26736                     pos += lines[i].length;
26737                     
26738                     if(i != 0){
26739                         pos += 1;
26740                     }
26741                     
26742                     if(pos < curr){
26743                         continue;
26744                     }
26745                     
26746                     break;
26747                 }
26748                 
26749                 if(!e.shiftKey){
26750                     this.el.dom.setSelectionRange(pos, pos);
26751                     return;
26752                 }
26753                 
26754                 this.el.dom.selectionStart = curr;
26755                 this.el.dom.selectionEnd = pos;
26756             },
26757
26758             scope : this,
26759
26760             doRelay : function(foo, bar, hname){
26761                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26762             },
26763
26764             forceKeyDown: true
26765         });
26766         
26767 //        if(this.autosave && this.w){
26768 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26769 //        }
26770     },
26771
26772     // private
26773     onResize : function(w, h)
26774     {
26775         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26776         var ew = false;
26777         var eh = false;
26778         
26779         if(this.el ){
26780             if(typeof w == 'number'){
26781                 var aw = w - this.wrap.getFrameWidth('lr');
26782                 this.el.setWidth(this.adjustWidth('textarea', aw));
26783                 ew = aw;
26784             }
26785             if(typeof h == 'number'){
26786                 var tbh = 0;
26787                 for (var i =0; i < this.toolbars.length;i++) {
26788                     // fixme - ask toolbars for heights?
26789                     tbh += this.toolbars[i].tb.el.getHeight();
26790                     if (this.toolbars[i].footer) {
26791                         tbh += this.toolbars[i].footer.el.getHeight();
26792                     }
26793                 }
26794                 
26795                 
26796                 
26797                 
26798                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26799                 ah -= 5; // knock a few pixes off for look..
26800 //                Roo.log(ah);
26801                 this.el.setHeight(this.adjustWidth('textarea', ah));
26802                 var eh = ah;
26803             }
26804         }
26805         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26806         this.editorcore.onResize(ew,eh);
26807         
26808     },
26809
26810     /**
26811      * Toggles the editor between standard and source edit mode.
26812      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26813      */
26814     toggleSourceEdit : function(sourceEditMode)
26815     {
26816         this.editorcore.toggleSourceEdit(sourceEditMode);
26817         
26818         if(this.editorcore.sourceEditMode){
26819             Roo.log('editor - showing textarea');
26820             
26821 //            Roo.log('in');
26822 //            Roo.log(this.syncValue());
26823             this.editorcore.syncValue();
26824             this.el.removeClass('x-hidden');
26825             this.el.dom.removeAttribute('tabIndex');
26826             this.el.focus();
26827             
26828             for (var i = 0; i < this.toolbars.length; i++) {
26829                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26830                     this.toolbars[i].tb.hide();
26831                     this.toolbars[i].footer.hide();
26832                 }
26833             }
26834             
26835         }else{
26836             Roo.log('editor - hiding textarea');
26837 //            Roo.log('out')
26838 //            Roo.log(this.pushValue()); 
26839             this.editorcore.pushValue();
26840             
26841             this.el.addClass('x-hidden');
26842             this.el.dom.setAttribute('tabIndex', -1);
26843             
26844             for (var i = 0; i < this.toolbars.length; i++) {
26845                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26846                     this.toolbars[i].tb.show();
26847                     this.toolbars[i].footer.show();
26848                 }
26849             }
26850             
26851             //this.deferFocus();
26852         }
26853         
26854         this.setSize(this.wrap.getSize());
26855         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26856         
26857         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26858     },
26859  
26860     // private (for BoxComponent)
26861     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26862
26863     // private (for BoxComponent)
26864     getResizeEl : function(){
26865         return this.wrap;
26866     },
26867
26868     // private (for BoxComponent)
26869     getPositionEl : function(){
26870         return this.wrap;
26871     },
26872
26873     // private
26874     initEvents : function(){
26875         this.originalValue = this.getValue();
26876     },
26877
26878     /**
26879      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26880      * @method
26881      */
26882     markInvalid : Roo.emptyFn,
26883     /**
26884      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26885      * @method
26886      */
26887     clearInvalid : Roo.emptyFn,
26888
26889     setValue : function(v){
26890         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26891         this.editorcore.pushValue();
26892     },
26893
26894      
26895     // private
26896     deferFocus : function(){
26897         this.focus.defer(10, this);
26898     },
26899
26900     // doc'ed in Field
26901     focus : function(){
26902         this.editorcore.focus();
26903         
26904     },
26905       
26906
26907     // private
26908     onDestroy : function(){
26909         
26910         
26911         
26912         if(this.rendered){
26913             
26914             for (var i =0; i < this.toolbars.length;i++) {
26915                 // fixme - ask toolbars for heights?
26916                 this.toolbars[i].onDestroy();
26917             }
26918             
26919             this.wrap.dom.innerHTML = '';
26920             this.wrap.remove();
26921         }
26922     },
26923
26924     // private
26925     onFirstFocus : function(){
26926         //Roo.log("onFirstFocus");
26927         this.editorcore.onFirstFocus();
26928          for (var i =0; i < this.toolbars.length;i++) {
26929             this.toolbars[i].onFirstFocus();
26930         }
26931         
26932     },
26933     
26934     // private
26935     syncValue : function()
26936     {
26937         this.editorcore.syncValue();
26938     },
26939     
26940     pushValue : function()
26941     {
26942         this.editorcore.pushValue();
26943     },
26944     
26945     setStylesheets : function(stylesheets)
26946     {
26947         this.editorcore.setStylesheets(stylesheets);
26948     },
26949     
26950     removeStylesheets : function()
26951     {
26952         this.editorcore.removeStylesheets();
26953     }
26954      
26955     
26956     // hide stuff that is not compatible
26957     /**
26958      * @event blur
26959      * @hide
26960      */
26961     /**
26962      * @event change
26963      * @hide
26964      */
26965     /**
26966      * @event focus
26967      * @hide
26968      */
26969     /**
26970      * @event specialkey
26971      * @hide
26972      */
26973     /**
26974      * @cfg {String} fieldClass @hide
26975      */
26976     /**
26977      * @cfg {String} focusClass @hide
26978      */
26979     /**
26980      * @cfg {String} autoCreate @hide
26981      */
26982     /**
26983      * @cfg {String} inputType @hide
26984      */
26985     /**
26986      * @cfg {String} invalidClass @hide
26987      */
26988     /**
26989      * @cfg {String} invalidText @hide
26990      */
26991     /**
26992      * @cfg {String} msgFx @hide
26993      */
26994     /**
26995      * @cfg {String} validateOnBlur @hide
26996      */
26997 });
26998  
26999     // <script type="text/javascript">
27000 /*
27001  * Based on
27002  * Ext JS Library 1.1.1
27003  * Copyright(c) 2006-2007, Ext JS, LLC.
27004  *  
27005  
27006  */
27007
27008 /**
27009  * @class Roo.form.HtmlEditorToolbar1
27010  * Basic Toolbar
27011  * 
27012  * Usage:
27013  *
27014  new Roo.form.HtmlEditor({
27015     ....
27016     toolbars : [
27017         new Roo.form.HtmlEditorToolbar1({
27018             disable : { fonts: 1 , format: 1, ..., ... , ...],
27019             btns : [ .... ]
27020         })
27021     }
27022      
27023  * 
27024  * @cfg {Object} disable List of elements to disable..
27025  * @cfg {Array} btns List of additional buttons.
27026  * 
27027  * 
27028  * NEEDS Extra CSS? 
27029  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27030  */
27031  
27032 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27033 {
27034     
27035     Roo.apply(this, config);
27036     
27037     // default disabled, based on 'good practice'..
27038     this.disable = this.disable || {};
27039     Roo.applyIf(this.disable, {
27040         fontSize : true,
27041         colors : true,
27042         specialElements : true
27043     });
27044     
27045     
27046     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27047     // dont call parent... till later.
27048 }
27049
27050 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
27051     
27052     tb: false,
27053     
27054     rendered: false,
27055     
27056     editor : false,
27057     editorcore : false,
27058     /**
27059      * @cfg {Object} disable  List of toolbar elements to disable
27060          
27061      */
27062     disable : false,
27063     
27064     
27065      /**
27066      * @cfg {String} createLinkText The default text for the create link prompt
27067      */
27068     createLinkText : 'Please enter the URL for the link:',
27069     /**
27070      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27071      */
27072     defaultLinkValue : 'http:/'+'/',
27073    
27074     
27075       /**
27076      * @cfg {Array} fontFamilies An array of available font families
27077      */
27078     fontFamilies : [
27079         'Arial',
27080         'Courier New',
27081         'Tahoma',
27082         'Times New Roman',
27083         'Verdana'
27084     ],
27085     
27086     specialChars : [
27087            "&#169;",
27088           "&#174;",     
27089           "&#8482;",    
27090           "&#163;" ,    
27091          // "&#8212;",    
27092           "&#8230;",    
27093           "&#247;" ,    
27094         //  "&#225;" ,     ?? a acute?
27095            "&#8364;"    , //Euro
27096        //   "&#8220;"    ,
27097         //  "&#8221;"    ,
27098         //  "&#8226;"    ,
27099           "&#176;"  //   , // degrees
27100
27101          // "&#233;"     , // e ecute
27102          // "&#250;"     , // u ecute?
27103     ],
27104     
27105     specialElements : [
27106         {
27107             text: "Insert Table",
27108             xtype: 'MenuItem',
27109             xns : Roo.Menu,
27110             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27111                 
27112         },
27113         {    
27114             text: "Insert Image",
27115             xtype: 'MenuItem',
27116             xns : Roo.Menu,
27117             ihtml : '<img src="about:blank"/>'
27118             
27119         }
27120         
27121          
27122     ],
27123     
27124     
27125     inputElements : [ 
27126             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27127             "input:submit", "input:button", "select", "textarea", "label" ],
27128     formats : [
27129         ["p"] ,  
27130         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27131         ["pre"],[ "code"], 
27132         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27133         ['div'],['span']
27134     ],
27135     
27136     cleanStyles : [
27137         "font-size"
27138     ],
27139      /**
27140      * @cfg {String} defaultFont default font to use.
27141      */
27142     defaultFont: 'tahoma',
27143    
27144     fontSelect : false,
27145     
27146     
27147     formatCombo : false,
27148     
27149     init : function(editor)
27150     {
27151         this.editor = editor;
27152         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27153         var editorcore = this.editorcore;
27154         
27155         var _t = this;
27156         
27157         var fid = editorcore.frameId;
27158         var etb = this;
27159         function btn(id, toggle, handler){
27160             var xid = fid + '-'+ id ;
27161             return {
27162                 id : xid,
27163                 cmd : id,
27164                 cls : 'x-btn-icon x-edit-'+id,
27165                 enableToggle:toggle !== false,
27166                 scope: _t, // was editor...
27167                 handler:handler||_t.relayBtnCmd,
27168                 clickEvent:'mousedown',
27169                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27170                 tabIndex:-1
27171             };
27172         }
27173         
27174         
27175         
27176         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27177         this.tb = tb;
27178          // stop form submits
27179         tb.el.on('click', function(e){
27180             e.preventDefault(); // what does this do?
27181         });
27182
27183         if(!this.disable.font) { // && !Roo.isSafari){
27184             /* why no safari for fonts 
27185             editor.fontSelect = tb.el.createChild({
27186                 tag:'select',
27187                 tabIndex: -1,
27188                 cls:'x-font-select',
27189                 html: this.createFontOptions()
27190             });
27191             
27192             editor.fontSelect.on('change', function(){
27193                 var font = editor.fontSelect.dom.value;
27194                 editor.relayCmd('fontname', font);
27195                 editor.deferFocus();
27196             }, editor);
27197             
27198             tb.add(
27199                 editor.fontSelect.dom,
27200                 '-'
27201             );
27202             */
27203             
27204         };
27205         if(!this.disable.formats){
27206             this.formatCombo = new Roo.form.ComboBox({
27207                 store: new Roo.data.SimpleStore({
27208                     id : 'tag',
27209                     fields: ['tag'],
27210                     data : this.formats // from states.js
27211                 }),
27212                 blockFocus : true,
27213                 name : '',
27214                 //autoCreate : {tag: "div",  size: "20"},
27215                 displayField:'tag',
27216                 typeAhead: false,
27217                 mode: 'local',
27218                 editable : false,
27219                 triggerAction: 'all',
27220                 emptyText:'Add tag',
27221                 selectOnFocus:true,
27222                 width:135,
27223                 listeners : {
27224                     'select': function(c, r, i) {
27225                         editorcore.insertTag(r.get('tag'));
27226                         editor.focus();
27227                     }
27228                 }
27229
27230             });
27231             tb.addField(this.formatCombo);
27232             
27233         }
27234         
27235         if(!this.disable.format){
27236             tb.add(
27237                 btn('bold'),
27238                 btn('italic'),
27239                 btn('underline'),
27240                 btn('strikethrough')
27241             );
27242         };
27243         if(!this.disable.fontSize){
27244             tb.add(
27245                 '-',
27246                 
27247                 
27248                 btn('increasefontsize', false, editorcore.adjustFont),
27249                 btn('decreasefontsize', false, editorcore.adjustFont)
27250             );
27251         };
27252         
27253         
27254         if(!this.disable.colors){
27255             tb.add(
27256                 '-', {
27257                     id:editorcore.frameId +'-forecolor',
27258                     cls:'x-btn-icon x-edit-forecolor',
27259                     clickEvent:'mousedown',
27260                     tooltip: this.buttonTips['forecolor'] || undefined,
27261                     tabIndex:-1,
27262                     menu : new Roo.menu.ColorMenu({
27263                         allowReselect: true,
27264                         focus: Roo.emptyFn,
27265                         value:'000000',
27266                         plain:true,
27267                         selectHandler: function(cp, color){
27268                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27269                             editor.deferFocus();
27270                         },
27271                         scope: editorcore,
27272                         clickEvent:'mousedown'
27273                     })
27274                 }, {
27275                     id:editorcore.frameId +'backcolor',
27276                     cls:'x-btn-icon x-edit-backcolor',
27277                     clickEvent:'mousedown',
27278                     tooltip: this.buttonTips['backcolor'] || undefined,
27279                     tabIndex:-1,
27280                     menu : new Roo.menu.ColorMenu({
27281                         focus: Roo.emptyFn,
27282                         value:'FFFFFF',
27283                         plain:true,
27284                         allowReselect: true,
27285                         selectHandler: function(cp, color){
27286                             if(Roo.isGecko){
27287                                 editorcore.execCmd('useCSS', false);
27288                                 editorcore.execCmd('hilitecolor', color);
27289                                 editorcore.execCmd('useCSS', true);
27290                                 editor.deferFocus();
27291                             }else{
27292                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27293                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27294                                 editor.deferFocus();
27295                             }
27296                         },
27297                         scope:editorcore,
27298                         clickEvent:'mousedown'
27299                     })
27300                 }
27301             );
27302         };
27303         // now add all the items...
27304         
27305
27306         if(!this.disable.alignments){
27307             tb.add(
27308                 '-',
27309                 btn('justifyleft'),
27310                 btn('justifycenter'),
27311                 btn('justifyright')
27312             );
27313         };
27314
27315         //if(!Roo.isSafari){
27316             if(!this.disable.links){
27317                 tb.add(
27318                     '-',
27319                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27320                 );
27321             };
27322
27323             if(!this.disable.lists){
27324                 tb.add(
27325                     '-',
27326                     btn('insertorderedlist'),
27327                     btn('insertunorderedlist')
27328                 );
27329             }
27330             if(!this.disable.sourceEdit){
27331                 tb.add(
27332                     '-',
27333                     btn('sourceedit', true, function(btn){
27334                         this.toggleSourceEdit(btn.pressed);
27335                     })
27336                 );
27337             }
27338         //}
27339         
27340         var smenu = { };
27341         // special menu.. - needs to be tidied up..
27342         if (!this.disable.special) {
27343             smenu = {
27344                 text: "&#169;",
27345                 cls: 'x-edit-none',
27346                 
27347                 menu : {
27348                     items : []
27349                 }
27350             };
27351             for (var i =0; i < this.specialChars.length; i++) {
27352                 smenu.menu.items.push({
27353                     
27354                     html: this.specialChars[i],
27355                     handler: function(a,b) {
27356                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27357                         //editor.insertAtCursor(a.html);
27358                         
27359                     },
27360                     tabIndex:-1
27361                 });
27362             }
27363             
27364             
27365             tb.add(smenu);
27366             
27367             
27368         }
27369         
27370         var cmenu = { };
27371         if (!this.disable.cleanStyles) {
27372             cmenu = {
27373                 cls: 'x-btn-icon x-btn-clear',
27374                 
27375                 menu : {
27376                     items : []
27377                 }
27378             };
27379             for (var i =0; i < this.cleanStyles.length; i++) {
27380                 cmenu.menu.items.push({
27381                     actiontype : this.cleanStyles[i],
27382                     html: 'Remove ' + this.cleanStyles[i],
27383                     handler: function(a,b) {
27384 //                        Roo.log(a);
27385 //                        Roo.log(b);
27386                         var c = Roo.get(editorcore.doc.body);
27387                         c.select('[style]').each(function(s) {
27388                             s.dom.style.removeProperty(a.actiontype);
27389                         });
27390                         editorcore.syncValue();
27391                     },
27392                     tabIndex:-1
27393                 });
27394             }
27395              cmenu.menu.items.push({
27396                 actiontype : 'tablewidths',
27397                 html: 'Remove Table Widths',
27398                 handler: function(a,b) {
27399                     editorcore.cleanTableWidths();
27400                     editorcore.syncValue();
27401                 },
27402                 tabIndex:-1
27403             });
27404             cmenu.menu.items.push({
27405                 actiontype : 'word',
27406                 html: 'Remove MS Word Formating',
27407                 handler: function(a,b) {
27408                     editorcore.cleanWord();
27409                     editorcore.syncValue();
27410                 },
27411                 tabIndex:-1
27412             });
27413             
27414             cmenu.menu.items.push({
27415                 actiontype : 'all',
27416                 html: 'Remove All Styles',
27417                 handler: function(a,b) {
27418                     
27419                     var c = Roo.get(editorcore.doc.body);
27420                     c.select('[style]').each(function(s) {
27421                         s.dom.removeAttribute('style');
27422                     });
27423                     editorcore.syncValue();
27424                 },
27425                 tabIndex:-1
27426             });
27427             
27428             cmenu.menu.items.push({
27429                 actiontype : 'all',
27430                 html: 'Remove All CSS Classes',
27431                 handler: function(a,b) {
27432                     
27433                     var c = Roo.get(editorcore.doc.body);
27434                     c.select('[class]').each(function(s) {
27435                         s.dom.className = '';
27436                     });
27437                     editorcore.syncValue();
27438                 },
27439                 tabIndex:-1
27440             });
27441             
27442              cmenu.menu.items.push({
27443                 actiontype : 'tidy',
27444                 html: 'Tidy HTML Source',
27445                 handler: function(a,b) {
27446                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27447                     editorcore.syncValue();
27448                 },
27449                 tabIndex:-1
27450             });
27451             
27452             
27453             tb.add(cmenu);
27454         }
27455          
27456         if (!this.disable.specialElements) {
27457             var semenu = {
27458                 text: "Other;",
27459                 cls: 'x-edit-none',
27460                 menu : {
27461                     items : []
27462                 }
27463             };
27464             for (var i =0; i < this.specialElements.length; i++) {
27465                 semenu.menu.items.push(
27466                     Roo.apply({ 
27467                         handler: function(a,b) {
27468                             editor.insertAtCursor(this.ihtml);
27469                         }
27470                     }, this.specialElements[i])
27471                 );
27472                     
27473             }
27474             
27475             tb.add(semenu);
27476             
27477             
27478         }
27479          
27480         
27481         if (this.btns) {
27482             for(var i =0; i< this.btns.length;i++) {
27483                 var b = Roo.factory(this.btns[i],Roo.form);
27484                 b.cls =  'x-edit-none';
27485                 
27486                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27487                     b.cls += ' x-init-enable';
27488                 }
27489                 
27490                 b.scope = editorcore;
27491                 tb.add(b);
27492             }
27493         
27494         }
27495         
27496         
27497         
27498         // disable everything...
27499         
27500         this.tb.items.each(function(item){
27501             
27502            if(
27503                 item.id != editorcore.frameId+ '-sourceedit' && 
27504                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27505             ){
27506                 
27507                 item.disable();
27508             }
27509         });
27510         this.rendered = true;
27511         
27512         // the all the btns;
27513         editor.on('editorevent', this.updateToolbar, this);
27514         // other toolbars need to implement this..
27515         //editor.on('editmodechange', this.updateToolbar, this);
27516     },
27517     
27518     
27519     relayBtnCmd : function(btn) {
27520         this.editorcore.relayCmd(btn.cmd);
27521     },
27522     // private used internally
27523     createLink : function(){
27524         Roo.log("create link?");
27525         var url = prompt(this.createLinkText, this.defaultLinkValue);
27526         if(url && url != 'http:/'+'/'){
27527             this.editorcore.relayCmd('createlink', url);
27528         }
27529     },
27530
27531     
27532     /**
27533      * Protected method that will not generally be called directly. It triggers
27534      * a toolbar update by reading the markup state of the current selection in the editor.
27535      */
27536     updateToolbar: function(){
27537
27538         if(!this.editorcore.activated){
27539             this.editor.onFirstFocus();
27540             return;
27541         }
27542
27543         var btns = this.tb.items.map, 
27544             doc = this.editorcore.doc,
27545             frameId = this.editorcore.frameId;
27546
27547         if(!this.disable.font && !Roo.isSafari){
27548             /*
27549             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27550             if(name != this.fontSelect.dom.value){
27551                 this.fontSelect.dom.value = name;
27552             }
27553             */
27554         }
27555         if(!this.disable.format){
27556             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27557             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27558             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27559             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27560         }
27561         if(!this.disable.alignments){
27562             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27563             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27564             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27565         }
27566         if(!Roo.isSafari && !this.disable.lists){
27567             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27568             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27569         }
27570         
27571         var ans = this.editorcore.getAllAncestors();
27572         if (this.formatCombo) {
27573             
27574             
27575             var store = this.formatCombo.store;
27576             this.formatCombo.setValue("");
27577             for (var i =0; i < ans.length;i++) {
27578                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27579                     // select it..
27580                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27581                     break;
27582                 }
27583             }
27584         }
27585         
27586         
27587         
27588         // hides menus... - so this cant be on a menu...
27589         Roo.menu.MenuMgr.hideAll();
27590
27591         //this.editorsyncValue();
27592     },
27593    
27594     
27595     createFontOptions : function(){
27596         var buf = [], fs = this.fontFamilies, ff, lc;
27597         
27598         
27599         
27600         for(var i = 0, len = fs.length; i< len; i++){
27601             ff = fs[i];
27602             lc = ff.toLowerCase();
27603             buf.push(
27604                 '<option value="',lc,'" style="font-family:',ff,';"',
27605                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27606                     ff,
27607                 '</option>'
27608             );
27609         }
27610         return buf.join('');
27611     },
27612     
27613     toggleSourceEdit : function(sourceEditMode){
27614         
27615         Roo.log("toolbar toogle");
27616         if(sourceEditMode === undefined){
27617             sourceEditMode = !this.sourceEditMode;
27618         }
27619         this.sourceEditMode = sourceEditMode === true;
27620         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27621         // just toggle the button?
27622         if(btn.pressed !== this.sourceEditMode){
27623             btn.toggle(this.sourceEditMode);
27624             return;
27625         }
27626         
27627         if(sourceEditMode){
27628             Roo.log("disabling buttons");
27629             this.tb.items.each(function(item){
27630                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27631                     item.disable();
27632                 }
27633             });
27634           
27635         }else{
27636             Roo.log("enabling buttons");
27637             if(this.editorcore.initialized){
27638                 this.tb.items.each(function(item){
27639                     item.enable();
27640                 });
27641             }
27642             
27643         }
27644         Roo.log("calling toggole on editor");
27645         // tell the editor that it's been pressed..
27646         this.editor.toggleSourceEdit(sourceEditMode);
27647        
27648     },
27649      /**
27650      * Object collection of toolbar tooltips for the buttons in the editor. The key
27651      * is the command id associated with that button and the value is a valid QuickTips object.
27652      * For example:
27653 <pre><code>
27654 {
27655     bold : {
27656         title: 'Bold (Ctrl+B)',
27657         text: 'Make the selected text bold.',
27658         cls: 'x-html-editor-tip'
27659     },
27660     italic : {
27661         title: 'Italic (Ctrl+I)',
27662         text: 'Make the selected text italic.',
27663         cls: 'x-html-editor-tip'
27664     },
27665     ...
27666 </code></pre>
27667     * @type Object
27668      */
27669     buttonTips : {
27670         bold : {
27671             title: 'Bold (Ctrl+B)',
27672             text: 'Make the selected text bold.',
27673             cls: 'x-html-editor-tip'
27674         },
27675         italic : {
27676             title: 'Italic (Ctrl+I)',
27677             text: 'Make the selected text italic.',
27678             cls: 'x-html-editor-tip'
27679         },
27680         underline : {
27681             title: 'Underline (Ctrl+U)',
27682             text: 'Underline the selected text.',
27683             cls: 'x-html-editor-tip'
27684         },
27685         strikethrough : {
27686             title: 'Strikethrough',
27687             text: 'Strikethrough the selected text.',
27688             cls: 'x-html-editor-tip'
27689         },
27690         increasefontsize : {
27691             title: 'Grow Text',
27692             text: 'Increase the font size.',
27693             cls: 'x-html-editor-tip'
27694         },
27695         decreasefontsize : {
27696             title: 'Shrink Text',
27697             text: 'Decrease the font size.',
27698             cls: 'x-html-editor-tip'
27699         },
27700         backcolor : {
27701             title: 'Text Highlight Color',
27702             text: 'Change the background color of the selected text.',
27703             cls: 'x-html-editor-tip'
27704         },
27705         forecolor : {
27706             title: 'Font Color',
27707             text: 'Change the color of the selected text.',
27708             cls: 'x-html-editor-tip'
27709         },
27710         justifyleft : {
27711             title: 'Align Text Left',
27712             text: 'Align text to the left.',
27713             cls: 'x-html-editor-tip'
27714         },
27715         justifycenter : {
27716             title: 'Center Text',
27717             text: 'Center text in the editor.',
27718             cls: 'x-html-editor-tip'
27719         },
27720         justifyright : {
27721             title: 'Align Text Right',
27722             text: 'Align text to the right.',
27723             cls: 'x-html-editor-tip'
27724         },
27725         insertunorderedlist : {
27726             title: 'Bullet List',
27727             text: 'Start a bulleted list.',
27728             cls: 'x-html-editor-tip'
27729         },
27730         insertorderedlist : {
27731             title: 'Numbered List',
27732             text: 'Start a numbered list.',
27733             cls: 'x-html-editor-tip'
27734         },
27735         createlink : {
27736             title: 'Hyperlink',
27737             text: 'Make the selected text a hyperlink.',
27738             cls: 'x-html-editor-tip'
27739         },
27740         sourceedit : {
27741             title: 'Source Edit',
27742             text: 'Switch to source editing mode.',
27743             cls: 'x-html-editor-tip'
27744         }
27745     },
27746     // private
27747     onDestroy : function(){
27748         if(this.rendered){
27749             
27750             this.tb.items.each(function(item){
27751                 if(item.menu){
27752                     item.menu.removeAll();
27753                     if(item.menu.el){
27754                         item.menu.el.destroy();
27755                     }
27756                 }
27757                 item.destroy();
27758             });
27759              
27760         }
27761     },
27762     onFirstFocus: function() {
27763         this.tb.items.each(function(item){
27764            item.enable();
27765         });
27766     }
27767 });
27768
27769
27770
27771
27772 // <script type="text/javascript">
27773 /*
27774  * Based on
27775  * Ext JS Library 1.1.1
27776  * Copyright(c) 2006-2007, Ext JS, LLC.
27777  *  
27778  
27779  */
27780
27781  
27782 /**
27783  * @class Roo.form.HtmlEditor.ToolbarContext
27784  * Context Toolbar
27785  * 
27786  * Usage:
27787  *
27788  new Roo.form.HtmlEditor({
27789     ....
27790     toolbars : [
27791         { xtype: 'ToolbarStandard', styles : {} }
27792         { xtype: 'ToolbarContext', disable : {} }
27793     ]
27794 })
27795
27796      
27797  * 
27798  * @config : {Object} disable List of elements to disable.. (not done yet.)
27799  * @config : {Object} styles  Map of styles available.
27800  * 
27801  */
27802
27803 Roo.form.HtmlEditor.ToolbarContext = function(config)
27804 {
27805     
27806     Roo.apply(this, config);
27807     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27808     // dont call parent... till later.
27809     this.styles = this.styles || {};
27810 }
27811
27812  
27813
27814 Roo.form.HtmlEditor.ToolbarContext.types = {
27815     'IMG' : {
27816         width : {
27817             title: "Width",
27818             width: 40
27819         },
27820         height:  {
27821             title: "Height",
27822             width: 40
27823         },
27824         align: {
27825             title: "Align",
27826             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27827             width : 80
27828             
27829         },
27830         border: {
27831             title: "Border",
27832             width: 40
27833         },
27834         alt: {
27835             title: "Alt",
27836             width: 120
27837         },
27838         src : {
27839             title: "Src",
27840             width: 220
27841         }
27842         
27843     },
27844     'A' : {
27845         name : {
27846             title: "Name",
27847             width: 50
27848         },
27849         target:  {
27850             title: "Target",
27851             width: 120
27852         },
27853         href:  {
27854             title: "Href",
27855             width: 220
27856         } // border?
27857         
27858     },
27859     'TABLE' : {
27860         rows : {
27861             title: "Rows",
27862             width: 20
27863         },
27864         cols : {
27865             title: "Cols",
27866             width: 20
27867         },
27868         width : {
27869             title: "Width",
27870             width: 40
27871         },
27872         height : {
27873             title: "Height",
27874             width: 40
27875         },
27876         border : {
27877             title: "Border",
27878             width: 20
27879         }
27880     },
27881     'TD' : {
27882         width : {
27883             title: "Width",
27884             width: 40
27885         },
27886         height : {
27887             title: "Height",
27888             width: 40
27889         },   
27890         align: {
27891             title: "Align",
27892             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27893             width: 80
27894         },
27895         valign: {
27896             title: "Valign",
27897             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27898             width: 80
27899         },
27900         colspan: {
27901             title: "Colspan",
27902             width: 20
27903             
27904         },
27905          'font-family'  : {
27906             title : "Font",
27907             style : 'fontFamily',
27908             displayField: 'display',
27909             optname : 'font-family',
27910             width: 140
27911         }
27912     },
27913     'INPUT' : {
27914         name : {
27915             title: "name",
27916             width: 120
27917         },
27918         value : {
27919             title: "Value",
27920             width: 120
27921         },
27922         width : {
27923             title: "Width",
27924             width: 40
27925         }
27926     },
27927     'LABEL' : {
27928         'for' : {
27929             title: "For",
27930             width: 120
27931         }
27932     },
27933     'TEXTAREA' : {
27934           name : {
27935             title: "name",
27936             width: 120
27937         },
27938         rows : {
27939             title: "Rows",
27940             width: 20
27941         },
27942         cols : {
27943             title: "Cols",
27944             width: 20
27945         }
27946     },
27947     'SELECT' : {
27948         name : {
27949             title: "name",
27950             width: 120
27951         },
27952         selectoptions : {
27953             title: "Options",
27954             width: 200
27955         }
27956     },
27957     
27958     // should we really allow this??
27959     // should this just be 
27960     'BODY' : {
27961         title : {
27962             title: "Title",
27963             width: 200,
27964             disabled : true
27965         }
27966     },
27967     'SPAN' : {
27968         'font-family'  : {
27969             title : "Font",
27970             style : 'fontFamily',
27971             displayField: 'display',
27972             optname : 'font-family',
27973             width: 140
27974         }
27975     },
27976     'DIV' : {
27977         'font-family'  : {
27978             title : "Font",
27979             style : 'fontFamily',
27980             displayField: 'display',
27981             optname : 'font-family',
27982             width: 140
27983         }
27984     },
27985      'P' : {
27986         'font-family'  : {
27987             title : "Font",
27988             style : 'fontFamily',
27989             displayField: 'display',
27990             optname : 'font-family',
27991             width: 140
27992         }
27993     },
27994     
27995     '*' : {
27996         // empty..
27997     }
27998
27999 };
28000
28001 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28002 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28003
28004 Roo.form.HtmlEditor.ToolbarContext.options = {
28005         'font-family'  : [ 
28006                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28007                 [ 'Courier New', 'Courier New'],
28008                 [ 'Tahoma', 'Tahoma'],
28009                 [ 'Times New Roman,serif', 'Times'],
28010                 [ 'Verdana','Verdana' ]
28011         ]
28012 };
28013
28014 // fixme - these need to be configurable..
28015  
28016
28017 //Roo.form.HtmlEditor.ToolbarContext.types
28018
28019
28020 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28021     
28022     tb: false,
28023     
28024     rendered: false,
28025     
28026     editor : false,
28027     editorcore : false,
28028     /**
28029      * @cfg {Object} disable  List of toolbar elements to disable
28030          
28031      */
28032     disable : false,
28033     /**
28034      * @cfg {Object} styles List of styles 
28035      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28036      *
28037      * These must be defined in the page, so they get rendered correctly..
28038      * .headline { }
28039      * TD.underline { }
28040      * 
28041      */
28042     styles : false,
28043     
28044     options: false,
28045     
28046     toolbars : false,
28047     
28048     init : function(editor)
28049     {
28050         this.editor = editor;
28051         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28052         var editorcore = this.editorcore;
28053         
28054         var fid = editorcore.frameId;
28055         var etb = this;
28056         function btn(id, toggle, handler){
28057             var xid = fid + '-'+ id ;
28058             return {
28059                 id : xid,
28060                 cmd : id,
28061                 cls : 'x-btn-icon x-edit-'+id,
28062                 enableToggle:toggle !== false,
28063                 scope: editorcore, // was editor...
28064                 handler:handler||editorcore.relayBtnCmd,
28065                 clickEvent:'mousedown',
28066                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28067                 tabIndex:-1
28068             };
28069         }
28070         // create a new element.
28071         var wdiv = editor.wrap.createChild({
28072                 tag: 'div'
28073             }, editor.wrap.dom.firstChild.nextSibling, true);
28074         
28075         // can we do this more than once??
28076         
28077          // stop form submits
28078       
28079  
28080         // disable everything...
28081         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28082         this.toolbars = {};
28083            
28084         for (var i in  ty) {
28085           
28086             this.toolbars[i] = this.buildToolbar(ty[i],i);
28087         }
28088         this.tb = this.toolbars.BODY;
28089         this.tb.el.show();
28090         this.buildFooter();
28091         this.footer.show();
28092         editor.on('hide', function( ) { this.footer.hide() }, this);
28093         editor.on('show', function( ) { this.footer.show() }, this);
28094         
28095          
28096         this.rendered = true;
28097         
28098         // the all the btns;
28099         editor.on('editorevent', this.updateToolbar, this);
28100         // other toolbars need to implement this..
28101         //editor.on('editmodechange', this.updateToolbar, this);
28102     },
28103     
28104     
28105     
28106     /**
28107      * Protected method that will not generally be called directly. It triggers
28108      * a toolbar update by reading the markup state of the current selection in the editor.
28109      *
28110      * Note you can force an update by calling on('editorevent', scope, false)
28111      */
28112     updateToolbar: function(editor,ev,sel){
28113
28114         //Roo.log(ev);
28115         // capture mouse up - this is handy for selecting images..
28116         // perhaps should go somewhere else...
28117         if(!this.editorcore.activated){
28118              this.editor.onFirstFocus();
28119             return;
28120         }
28121         
28122         
28123         
28124         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28125         // selectNode - might want to handle IE?
28126         if (ev &&
28127             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28128             ev.target && ev.target.tagName == 'IMG') {
28129             // they have click on an image...
28130             // let's see if we can change the selection...
28131             sel = ev.target;
28132          
28133               var nodeRange = sel.ownerDocument.createRange();
28134             try {
28135                 nodeRange.selectNode(sel);
28136             } catch (e) {
28137                 nodeRange.selectNodeContents(sel);
28138             }
28139             //nodeRange.collapse(true);
28140             var s = this.editorcore.win.getSelection();
28141             s.removeAllRanges();
28142             s.addRange(nodeRange);
28143         }  
28144         
28145       
28146         var updateFooter = sel ? false : true;
28147         
28148         
28149         var ans = this.editorcore.getAllAncestors();
28150         
28151         // pick
28152         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28153         
28154         if (!sel) { 
28155             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28156             sel = sel ? sel : this.editorcore.doc.body;
28157             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28158             
28159         }
28160         // pick a menu that exists..
28161         var tn = sel.tagName.toUpperCase();
28162         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28163         
28164         tn = sel.tagName.toUpperCase();
28165         
28166         var lastSel = this.tb.selectedNode;
28167         
28168         this.tb.selectedNode = sel;
28169         
28170         // if current menu does not match..
28171         
28172         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28173                 
28174             this.tb.el.hide();
28175             ///console.log("show: " + tn);
28176             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28177             this.tb.el.show();
28178             // update name
28179             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28180             
28181             
28182             // update attributes
28183             if (this.tb.fields) {
28184                 this.tb.fields.each(function(e) {
28185                     if (e.stylename) {
28186                         e.setValue(sel.style[e.stylename]);
28187                         return;
28188                     } 
28189                    e.setValue(sel.getAttribute(e.attrname));
28190                 });
28191             }
28192             
28193             var hasStyles = false;
28194             for(var i in this.styles) {
28195                 hasStyles = true;
28196                 break;
28197             }
28198             
28199             // update styles
28200             if (hasStyles) { 
28201                 var st = this.tb.fields.item(0);
28202                 
28203                 st.store.removeAll();
28204                
28205                 
28206                 var cn = sel.className.split(/\s+/);
28207                 
28208                 var avs = [];
28209                 if (this.styles['*']) {
28210                     
28211                     Roo.each(this.styles['*'], function(v) {
28212                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28213                     });
28214                 }
28215                 if (this.styles[tn]) { 
28216                     Roo.each(this.styles[tn], function(v) {
28217                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28218                     });
28219                 }
28220                 
28221                 st.store.loadData(avs);
28222                 st.collapse();
28223                 st.setValue(cn);
28224             }
28225             // flag our selected Node.
28226             this.tb.selectedNode = sel;
28227            
28228            
28229             Roo.menu.MenuMgr.hideAll();
28230
28231         }
28232         
28233         if (!updateFooter) {
28234             //this.footDisp.dom.innerHTML = ''; 
28235             return;
28236         }
28237         // update the footer
28238         //
28239         var html = '';
28240         
28241         this.footerEls = ans.reverse();
28242         Roo.each(this.footerEls, function(a,i) {
28243             if (!a) { return; }
28244             html += html.length ? ' &gt; '  :  '';
28245             
28246             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28247             
28248         });
28249        
28250         // 
28251         var sz = this.footDisp.up('td').getSize();
28252         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28253         this.footDisp.dom.style.marginLeft = '5px';
28254         
28255         this.footDisp.dom.style.overflow = 'hidden';
28256         
28257         this.footDisp.dom.innerHTML = html;
28258             
28259         //this.editorsyncValue();
28260     },
28261      
28262     
28263    
28264        
28265     // private
28266     onDestroy : function(){
28267         if(this.rendered){
28268             
28269             this.tb.items.each(function(item){
28270                 if(item.menu){
28271                     item.menu.removeAll();
28272                     if(item.menu.el){
28273                         item.menu.el.destroy();
28274                     }
28275                 }
28276                 item.destroy();
28277             });
28278              
28279         }
28280     },
28281     onFirstFocus: function() {
28282         // need to do this for all the toolbars..
28283         this.tb.items.each(function(item){
28284            item.enable();
28285         });
28286     },
28287     buildToolbar: function(tlist, nm)
28288     {
28289         var editor = this.editor;
28290         var editorcore = this.editorcore;
28291          // create a new element.
28292         var wdiv = editor.wrap.createChild({
28293                 tag: 'div'
28294             }, editor.wrap.dom.firstChild.nextSibling, true);
28295         
28296        
28297         var tb = new Roo.Toolbar(wdiv);
28298         // add the name..
28299         
28300         tb.add(nm+ ":&nbsp;");
28301         
28302         var styles = [];
28303         for(var i in this.styles) {
28304             styles.push(i);
28305         }
28306         
28307         // styles...
28308         if (styles && styles.length) {
28309             
28310             // this needs a multi-select checkbox...
28311             tb.addField( new Roo.form.ComboBox({
28312                 store: new Roo.data.SimpleStore({
28313                     id : 'val',
28314                     fields: ['val', 'selected'],
28315                     data : [] 
28316                 }),
28317                 name : '-roo-edit-className',
28318                 attrname : 'className',
28319                 displayField: 'val',
28320                 typeAhead: false,
28321                 mode: 'local',
28322                 editable : false,
28323                 triggerAction: 'all',
28324                 emptyText:'Select Style',
28325                 selectOnFocus:true,
28326                 width: 130,
28327                 listeners : {
28328                     'select': function(c, r, i) {
28329                         // initial support only for on class per el..
28330                         tb.selectedNode.className =  r ? r.get('val') : '';
28331                         editorcore.syncValue();
28332                     }
28333                 }
28334     
28335             }));
28336         }
28337         
28338         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28339         var tbops = tbc.options;
28340         
28341         for (var i in tlist) {
28342             
28343             var item = tlist[i];
28344             tb.add(item.title + ":&nbsp;");
28345             
28346             
28347             //optname == used so you can configure the options available..
28348             var opts = item.opts ? item.opts : false;
28349             if (item.optname) {
28350                 opts = tbops[item.optname];
28351            
28352             }
28353             
28354             if (opts) {
28355                 // opts == pulldown..
28356                 tb.addField( new Roo.form.ComboBox({
28357                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28358                         id : 'val',
28359                         fields: ['val', 'display'],
28360                         data : opts  
28361                     }),
28362                     name : '-roo-edit-' + i,
28363                     attrname : i,
28364                     stylename : item.style ? item.style : false,
28365                     displayField: item.displayField ? item.displayField : 'val',
28366                     valueField :  'val',
28367                     typeAhead: false,
28368                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28369                     editable : false,
28370                     triggerAction: 'all',
28371                     emptyText:'Select',
28372                     selectOnFocus:true,
28373                     width: item.width ? item.width  : 130,
28374                     listeners : {
28375                         'select': function(c, r, i) {
28376                             if (c.stylename) {
28377                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28378                                 return;
28379                             }
28380                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28381                         }
28382                     }
28383
28384                 }));
28385                 continue;
28386                     
28387                  
28388                 
28389                 tb.addField( new Roo.form.TextField({
28390                     name: i,
28391                     width: 100,
28392                     //allowBlank:false,
28393                     value: ''
28394                 }));
28395                 continue;
28396             }
28397             tb.addField( new Roo.form.TextField({
28398                 name: '-roo-edit-' + i,
28399                 attrname : i,
28400                 
28401                 width: item.width,
28402                 //allowBlank:true,
28403                 value: '',
28404                 listeners: {
28405                     'change' : function(f, nv, ov) {
28406                         tb.selectedNode.setAttribute(f.attrname, nv);
28407                     }
28408                 }
28409             }));
28410              
28411         }
28412         
28413         var _this = this;
28414         
28415         if(nm == 'BODY'){
28416             tb.addSeparator();
28417         
28418             tb.addButton( {
28419                 text: 'Stylesheets',
28420
28421                 listeners : {
28422                     click : function ()
28423                     {
28424                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28425                     }
28426                 }
28427             });
28428         }
28429         
28430         tb.addFill();
28431         tb.addButton( {
28432             text: 'Remove Tag',
28433     
28434             listeners : {
28435                 click : function ()
28436                 {
28437                     // remove
28438                     // undo does not work.
28439                      
28440                     var sn = tb.selectedNode;
28441                     
28442                     var pn = sn.parentNode;
28443                     
28444                     var stn =  sn.childNodes[0];
28445                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28446                     while (sn.childNodes.length) {
28447                         var node = sn.childNodes[0];
28448                         sn.removeChild(node);
28449                         //Roo.log(node);
28450                         pn.insertBefore(node, sn);
28451                         
28452                     }
28453                     pn.removeChild(sn);
28454                     var range = editorcore.createRange();
28455         
28456                     range.setStart(stn,0);
28457                     range.setEnd(en,0); //????
28458                     //range.selectNode(sel);
28459                     
28460                     
28461                     var selection = editorcore.getSelection();
28462                     selection.removeAllRanges();
28463                     selection.addRange(range);
28464                     
28465                     
28466                     
28467                     //_this.updateToolbar(null, null, pn);
28468                     _this.updateToolbar(null, null, null);
28469                     _this.footDisp.dom.innerHTML = ''; 
28470                 }
28471             }
28472             
28473                     
28474                 
28475             
28476         });
28477         
28478         
28479         tb.el.on('click', function(e){
28480             e.preventDefault(); // what does this do?
28481         });
28482         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28483         tb.el.hide();
28484         tb.name = nm;
28485         // dont need to disable them... as they will get hidden
28486         return tb;
28487          
28488         
28489     },
28490     buildFooter : function()
28491     {
28492         
28493         var fel = this.editor.wrap.createChild();
28494         this.footer = new Roo.Toolbar(fel);
28495         // toolbar has scrolly on left / right?
28496         var footDisp= new Roo.Toolbar.Fill();
28497         var _t = this;
28498         this.footer.add(
28499             {
28500                 text : '&lt;',
28501                 xtype: 'Button',
28502                 handler : function() {
28503                     _t.footDisp.scrollTo('left',0,true)
28504                 }
28505             }
28506         );
28507         this.footer.add( footDisp );
28508         this.footer.add( 
28509             {
28510                 text : '&gt;',
28511                 xtype: 'Button',
28512                 handler : function() {
28513                     // no animation..
28514                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28515                 }
28516             }
28517         );
28518         var fel = Roo.get(footDisp.el);
28519         fel.addClass('x-editor-context');
28520         this.footDispWrap = fel; 
28521         this.footDispWrap.overflow  = 'hidden';
28522         
28523         this.footDisp = fel.createChild();
28524         this.footDispWrap.on('click', this.onContextClick, this)
28525         
28526         
28527     },
28528     onContextClick : function (ev,dom)
28529     {
28530         ev.preventDefault();
28531         var  cn = dom.className;
28532         //Roo.log(cn);
28533         if (!cn.match(/x-ed-loc-/)) {
28534             return;
28535         }
28536         var n = cn.split('-').pop();
28537         var ans = this.footerEls;
28538         var sel = ans[n];
28539         
28540          // pick
28541         var range = this.editorcore.createRange();
28542         
28543         range.selectNodeContents(sel);
28544         //range.selectNode(sel);
28545         
28546         
28547         var selection = this.editorcore.getSelection();
28548         selection.removeAllRanges();
28549         selection.addRange(range);
28550         
28551         
28552         
28553         this.updateToolbar(null, null, sel);
28554         
28555         
28556     }
28557     
28558     
28559     
28560     
28561     
28562 });
28563
28564
28565
28566
28567
28568 /*
28569  * Based on:
28570  * Ext JS Library 1.1.1
28571  * Copyright(c) 2006-2007, Ext JS, LLC.
28572  *
28573  * Originally Released Under LGPL - original licence link has changed is not relivant.
28574  *
28575  * Fork - LGPL
28576  * <script type="text/javascript">
28577  */
28578  
28579 /**
28580  * @class Roo.form.BasicForm
28581  * @extends Roo.util.Observable
28582  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28583  * @constructor
28584  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28585  * @param {Object} config Configuration options
28586  */
28587 Roo.form.BasicForm = function(el, config){
28588     this.allItems = [];
28589     this.childForms = [];
28590     Roo.apply(this, config);
28591     /*
28592      * The Roo.form.Field items in this form.
28593      * @type MixedCollection
28594      */
28595      
28596      
28597     this.items = new Roo.util.MixedCollection(false, function(o){
28598         return o.id || (o.id = Roo.id());
28599     });
28600     this.addEvents({
28601         /**
28602          * @event beforeaction
28603          * Fires before any action is performed. Return false to cancel the action.
28604          * @param {Form} this
28605          * @param {Action} action The action to be performed
28606          */
28607         beforeaction: true,
28608         /**
28609          * @event actionfailed
28610          * Fires when an action fails.
28611          * @param {Form} this
28612          * @param {Action} action The action that failed
28613          */
28614         actionfailed : true,
28615         /**
28616          * @event actioncomplete
28617          * Fires when an action is completed.
28618          * @param {Form} this
28619          * @param {Action} action The action that completed
28620          */
28621         actioncomplete : true
28622     });
28623     if(el){
28624         this.initEl(el);
28625     }
28626     Roo.form.BasicForm.superclass.constructor.call(this);
28627 };
28628
28629 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28630     /**
28631      * @cfg {String} method
28632      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28633      */
28634     /**
28635      * @cfg {DataReader} reader
28636      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28637      * This is optional as there is built-in support for processing JSON.
28638      */
28639     /**
28640      * @cfg {DataReader} errorReader
28641      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28642      * This is completely optional as there is built-in support for processing JSON.
28643      */
28644     /**
28645      * @cfg {String} url
28646      * The URL to use for form actions if one isn't supplied in the action options.
28647      */
28648     /**
28649      * @cfg {Boolean} fileUpload
28650      * Set to true if this form is a file upload.
28651      */
28652      
28653     /**
28654      * @cfg {Object} baseParams
28655      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28656      */
28657      /**
28658      
28659     /**
28660      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28661      */
28662     timeout: 30,
28663
28664     // private
28665     activeAction : null,
28666
28667     /**
28668      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28669      * or setValues() data instead of when the form was first created.
28670      */
28671     trackResetOnLoad : false,
28672     
28673     
28674     /**
28675      * childForms - used for multi-tab forms
28676      * @type {Array}
28677      */
28678     childForms : false,
28679     
28680     /**
28681      * allItems - full list of fields.
28682      * @type {Array}
28683      */
28684     allItems : false,
28685     
28686     /**
28687      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28688      * element by passing it or its id or mask the form itself by passing in true.
28689      * @type Mixed
28690      */
28691     waitMsgTarget : false,
28692
28693     // private
28694     initEl : function(el){
28695         this.el = Roo.get(el);
28696         this.id = this.el.id || Roo.id();
28697         this.el.on('submit', this.onSubmit, this);
28698         this.el.addClass('x-form');
28699     },
28700
28701     // private
28702     onSubmit : function(e){
28703         e.stopEvent();
28704     },
28705
28706     /**
28707      * Returns true if client-side validation on the form is successful.
28708      * @return Boolean
28709      */
28710     isValid : function(){
28711         var valid = true;
28712         this.items.each(function(f){
28713            if(!f.validate()){
28714                valid = false;
28715            }
28716         });
28717         return valid;
28718     },
28719
28720     /**
28721      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
28722      * @return Boolean
28723      */
28724     isDirty : function(){
28725         var dirty = false;
28726         this.items.each(function(f){
28727            if(f.isDirty()){
28728                dirty = true;
28729                return false;
28730            }
28731         });
28732         return dirty;
28733     },
28734     
28735     /**
28736      * Returns true if any fields in this form have changed since their original load. (New version)
28737      * @return Boolean
28738      */
28739     
28740     hasChanged : function()
28741     {
28742         var dirty = false;
28743         this.items.each(function(f){
28744            if(f.hasChanged()){
28745                dirty = true;
28746                return false;
28747            }
28748         });
28749         return dirty;
28750         
28751     },
28752     /**
28753      * Resets all hasChanged to 'false' -
28754      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
28755      * So hasChanged storage is only to be used for this purpose
28756      * @return Boolean
28757      */
28758     resetHasChanged : function()
28759     {
28760         this.items.each(function(f){
28761            f.resetHasChanged();
28762         });
28763         
28764     },
28765     
28766     
28767     /**
28768      * Performs a predefined action (submit or load) or custom actions you define on this form.
28769      * @param {String} actionName The name of the action type
28770      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28771      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28772      * accept other config options):
28773      * <pre>
28774 Property          Type             Description
28775 ----------------  ---------------  ----------------------------------------------------------------------------------
28776 url               String           The url for the action (defaults to the form's url)
28777 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28778 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28779 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28780                                    validate the form on the client (defaults to false)
28781      * </pre>
28782      * @return {BasicForm} this
28783      */
28784     doAction : function(action, options){
28785         if(typeof action == 'string'){
28786             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28787         }
28788         if(this.fireEvent('beforeaction', this, action) !== false){
28789             this.beforeAction(action);
28790             action.run.defer(100, action);
28791         }
28792         return this;
28793     },
28794
28795     /**
28796      * Shortcut to do a submit action.
28797      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28798      * @return {BasicForm} this
28799      */
28800     submit : function(options){
28801         this.doAction('submit', options);
28802         return this;
28803     },
28804
28805     /**
28806      * Shortcut to do a load action.
28807      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28808      * @return {BasicForm} this
28809      */
28810     load : function(options){
28811         this.doAction('load', options);
28812         return this;
28813     },
28814
28815     /**
28816      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28817      * @param {Record} record The record to edit
28818      * @return {BasicForm} this
28819      */
28820     updateRecord : function(record){
28821         record.beginEdit();
28822         var fs = record.fields;
28823         fs.each(function(f){
28824             var field = this.findField(f.name);
28825             if(field){
28826                 record.set(f.name, field.getValue());
28827             }
28828         }, this);
28829         record.endEdit();
28830         return this;
28831     },
28832
28833     /**
28834      * Loads an Roo.data.Record into this form.
28835      * @param {Record} record The record to load
28836      * @return {BasicForm} this
28837      */
28838     loadRecord : function(record){
28839         this.setValues(record.data);
28840         return this;
28841     },
28842
28843     // private
28844     beforeAction : function(action){
28845         var o = action.options;
28846         
28847        
28848         if(this.waitMsgTarget === true){
28849             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28850         }else if(this.waitMsgTarget){
28851             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28852             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28853         }else {
28854             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28855         }
28856          
28857     },
28858
28859     // private
28860     afterAction : function(action, success){
28861         this.activeAction = null;
28862         var o = action.options;
28863         
28864         if(this.waitMsgTarget === true){
28865             this.el.unmask();
28866         }else if(this.waitMsgTarget){
28867             this.waitMsgTarget.unmask();
28868         }else{
28869             Roo.MessageBox.updateProgress(1);
28870             Roo.MessageBox.hide();
28871         }
28872          
28873         if(success){
28874             if(o.reset){
28875                 this.reset();
28876             }
28877             Roo.callback(o.success, o.scope, [this, action]);
28878             this.fireEvent('actioncomplete', this, action);
28879             
28880         }else{
28881             
28882             // failure condition..
28883             // we have a scenario where updates need confirming.
28884             // eg. if a locking scenario exists..
28885             // we look for { errors : { needs_confirm : true }} in the response.
28886             if (
28887                 (typeof(action.result) != 'undefined')  &&
28888                 (typeof(action.result.errors) != 'undefined')  &&
28889                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28890            ){
28891                 var _t = this;
28892                 Roo.MessageBox.confirm(
28893                     "Change requires confirmation",
28894                     action.result.errorMsg,
28895                     function(r) {
28896                         if (r != 'yes') {
28897                             return;
28898                         }
28899                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28900                     }
28901                     
28902                 );
28903                 
28904                 
28905                 
28906                 return;
28907             }
28908             
28909             Roo.callback(o.failure, o.scope, [this, action]);
28910             // show an error message if no failed handler is set..
28911             if (!this.hasListener('actionfailed')) {
28912                 Roo.MessageBox.alert("Error",
28913                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28914                         action.result.errorMsg :
28915                         "Saving Failed, please check your entries or try again"
28916                 );
28917             }
28918             
28919             this.fireEvent('actionfailed', this, action);
28920         }
28921         
28922     },
28923
28924     /**
28925      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28926      * @param {String} id The value to search for
28927      * @return Field
28928      */
28929     findField : function(id){
28930         var field = this.items.get(id);
28931         if(!field){
28932             this.items.each(function(f){
28933                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28934                     field = f;
28935                     return false;
28936                 }
28937             });
28938         }
28939         return field || null;
28940     },
28941
28942     /**
28943      * Add a secondary form to this one, 
28944      * Used to provide tabbed forms. One form is primary, with hidden values 
28945      * which mirror the elements from the other forms.
28946      * 
28947      * @param {Roo.form.Form} form to add.
28948      * 
28949      */
28950     addForm : function(form)
28951     {
28952        
28953         if (this.childForms.indexOf(form) > -1) {
28954             // already added..
28955             return;
28956         }
28957         this.childForms.push(form);
28958         var n = '';
28959         Roo.each(form.allItems, function (fe) {
28960             
28961             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28962             if (this.findField(n)) { // already added..
28963                 return;
28964             }
28965             var add = new Roo.form.Hidden({
28966                 name : n
28967             });
28968             add.render(this.el);
28969             
28970             this.add( add );
28971         }, this);
28972         
28973     },
28974     /**
28975      * Mark fields in this form invalid in bulk.
28976      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28977      * @return {BasicForm} this
28978      */
28979     markInvalid : function(errors){
28980         if(errors instanceof Array){
28981             for(var i = 0, len = errors.length; i < len; i++){
28982                 var fieldError = errors[i];
28983                 var f = this.findField(fieldError.id);
28984                 if(f){
28985                     f.markInvalid(fieldError.msg);
28986                 }
28987             }
28988         }else{
28989             var field, id;
28990             for(id in errors){
28991                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28992                     field.markInvalid(errors[id]);
28993                 }
28994             }
28995         }
28996         Roo.each(this.childForms || [], function (f) {
28997             f.markInvalid(errors);
28998         });
28999         
29000         return this;
29001     },
29002
29003     /**
29004      * Set values for fields in this form in bulk.
29005      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29006      * @return {BasicForm} this
29007      */
29008     setValues : function(values){
29009         if(values instanceof Array){ // array of objects
29010             for(var i = 0, len = values.length; i < len; i++){
29011                 var v = values[i];
29012                 var f = this.findField(v.id);
29013                 if(f){
29014                     f.setValue(v.value);
29015                     if(this.trackResetOnLoad){
29016                         f.originalValue = f.getValue();
29017                     }
29018                 }
29019             }
29020         }else{ // object hash
29021             var field, id;
29022             for(id in values){
29023                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29024                     
29025                     if (field.setFromData && 
29026                         field.valueField && 
29027                         field.displayField &&
29028                         // combos' with local stores can 
29029                         // be queried via setValue()
29030                         // to set their value..
29031                         (field.store && !field.store.isLocal)
29032                         ) {
29033                         // it's a combo
29034                         var sd = { };
29035                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29036                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29037                         field.setFromData(sd);
29038                         
29039                     } else {
29040                         field.setValue(values[id]);
29041                     }
29042                     
29043                     
29044                     if(this.trackResetOnLoad){
29045                         field.originalValue = field.getValue();
29046                     }
29047                 }
29048             }
29049         }
29050         this.resetHasChanged();
29051         
29052         
29053         Roo.each(this.childForms || [], function (f) {
29054             f.setValues(values);
29055             f.resetHasChanged();
29056         });
29057                 
29058         return this;
29059     },
29060
29061     /**
29062      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29063      * they are returned as an array.
29064      * @param {Boolean} asString
29065      * @return {Object}
29066      */
29067     getValues : function(asString){
29068         if (this.childForms) {
29069             // copy values from the child forms
29070             Roo.each(this.childForms, function (f) {
29071                 this.setValues(f.getValues());
29072             }, this);
29073         }
29074         
29075         
29076         
29077         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29078         if(asString === true){
29079             return fs;
29080         }
29081         return Roo.urlDecode(fs);
29082     },
29083     
29084     /**
29085      * Returns the fields in this form as an object with key/value pairs. 
29086      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29087      * @return {Object}
29088      */
29089     getFieldValues : function(with_hidden)
29090     {
29091         if (this.childForms) {
29092             // copy values from the child forms
29093             // should this call getFieldValues - probably not as we do not currently copy
29094             // hidden fields when we generate..
29095             Roo.each(this.childForms, function (f) {
29096                 this.setValues(f.getValues());
29097             }, this);
29098         }
29099         
29100         var ret = {};
29101         this.items.each(function(f){
29102             if (!f.getName()) {
29103                 return;
29104             }
29105             var v = f.getValue();
29106             if (f.inputType =='radio') {
29107                 if (typeof(ret[f.getName()]) == 'undefined') {
29108                     ret[f.getName()] = ''; // empty..
29109                 }
29110                 
29111                 if (!f.el.dom.checked) {
29112                     return;
29113                     
29114                 }
29115                 v = f.el.dom.value;
29116                 
29117             }
29118             
29119             // not sure if this supported any more..
29120             if ((typeof(v) == 'object') && f.getRawValue) {
29121                 v = f.getRawValue() ; // dates..
29122             }
29123             // combo boxes where name != hiddenName...
29124             if (f.name != f.getName()) {
29125                 ret[f.name] = f.getRawValue();
29126             }
29127             ret[f.getName()] = v;
29128         });
29129         
29130         return ret;
29131     },
29132
29133     /**
29134      * Clears all invalid messages in this form.
29135      * @return {BasicForm} this
29136      */
29137     clearInvalid : function(){
29138         this.items.each(function(f){
29139            f.clearInvalid();
29140         });
29141         
29142         Roo.each(this.childForms || [], function (f) {
29143             f.clearInvalid();
29144         });
29145         
29146         
29147         return this;
29148     },
29149
29150     /**
29151      * Resets this form.
29152      * @return {BasicForm} this
29153      */
29154     reset : function(){
29155         this.items.each(function(f){
29156             f.reset();
29157         });
29158         
29159         Roo.each(this.childForms || [], function (f) {
29160             f.reset();
29161         });
29162         this.resetHasChanged();
29163         
29164         return this;
29165     },
29166
29167     /**
29168      * Add Roo.form components to this form.
29169      * @param {Field} field1
29170      * @param {Field} field2 (optional)
29171      * @param {Field} etc (optional)
29172      * @return {BasicForm} this
29173      */
29174     add : function(){
29175         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29176         return this;
29177     },
29178
29179
29180     /**
29181      * Removes a field from the items collection (does NOT remove its markup).
29182      * @param {Field} field
29183      * @return {BasicForm} this
29184      */
29185     remove : function(field){
29186         this.items.remove(field);
29187         return this;
29188     },
29189
29190     /**
29191      * Looks at the fields in this form, checks them for an id attribute,
29192      * and calls applyTo on the existing dom element with that id.
29193      * @return {BasicForm} this
29194      */
29195     render : function(){
29196         this.items.each(function(f){
29197             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29198                 f.applyTo(f.id);
29199             }
29200         });
29201         return this;
29202     },
29203
29204     /**
29205      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29206      * @param {Object} values
29207      * @return {BasicForm} this
29208      */
29209     applyToFields : function(o){
29210         this.items.each(function(f){
29211            Roo.apply(f, o);
29212         });
29213         return this;
29214     },
29215
29216     /**
29217      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29218      * @param {Object} values
29219      * @return {BasicForm} this
29220      */
29221     applyIfToFields : function(o){
29222         this.items.each(function(f){
29223            Roo.applyIf(f, o);
29224         });
29225         return this;
29226     }
29227 });
29228
29229 // back compat
29230 Roo.BasicForm = Roo.form.BasicForm;/*
29231  * Based on:
29232  * Ext JS Library 1.1.1
29233  * Copyright(c) 2006-2007, Ext JS, LLC.
29234  *
29235  * Originally Released Under LGPL - original licence link has changed is not relivant.
29236  *
29237  * Fork - LGPL
29238  * <script type="text/javascript">
29239  */
29240
29241 /**
29242  * @class Roo.form.Form
29243  * @extends Roo.form.BasicForm
29244  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29245  * @constructor
29246  * @param {Object} config Configuration options
29247  */
29248 Roo.form.Form = function(config){
29249     var xitems =  [];
29250     if (config.items) {
29251         xitems = config.items;
29252         delete config.items;
29253     }
29254    
29255     
29256     Roo.form.Form.superclass.constructor.call(this, null, config);
29257     this.url = this.url || this.action;
29258     if(!this.root){
29259         this.root = new Roo.form.Layout(Roo.applyIf({
29260             id: Roo.id()
29261         }, config));
29262     }
29263     this.active = this.root;
29264     /**
29265      * Array of all the buttons that have been added to this form via {@link addButton}
29266      * @type Array
29267      */
29268     this.buttons = [];
29269     this.allItems = [];
29270     this.addEvents({
29271         /**
29272          * @event clientvalidation
29273          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29274          * @param {Form} this
29275          * @param {Boolean} valid true if the form has passed client-side validation
29276          */
29277         clientvalidation: true,
29278         /**
29279          * @event rendered
29280          * Fires when the form is rendered
29281          * @param {Roo.form.Form} form
29282          */
29283         rendered : true
29284     });
29285     
29286     if (this.progressUrl) {
29287             // push a hidden field onto the list of fields..
29288             this.addxtype( {
29289                     xns: Roo.form, 
29290                     xtype : 'Hidden', 
29291                     name : 'UPLOAD_IDENTIFIER' 
29292             });
29293         }
29294         
29295     
29296     Roo.each(xitems, this.addxtype, this);
29297     
29298     
29299     
29300 };
29301
29302 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29303     /**
29304      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29305      */
29306     /**
29307      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29308      */
29309     /**
29310      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29311      */
29312     buttonAlign:'center',
29313
29314     /**
29315      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29316      */
29317     minButtonWidth:75,
29318
29319     /**
29320      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29321      * This property cascades to child containers if not set.
29322      */
29323     labelAlign:'left',
29324
29325     /**
29326      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29327      * fires a looping event with that state. This is required to bind buttons to the valid
29328      * state using the config value formBind:true on the button.
29329      */
29330     monitorValid : false,
29331
29332     /**
29333      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29334      */
29335     monitorPoll : 200,
29336     
29337     /**
29338      * @cfg {String} progressUrl - Url to return progress data 
29339      */
29340     
29341     progressUrl : false,
29342   
29343     /**
29344      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29345      * fields are added and the column is closed. If no fields are passed the column remains open
29346      * until end() is called.
29347      * @param {Object} config The config to pass to the column
29348      * @param {Field} field1 (optional)
29349      * @param {Field} field2 (optional)
29350      * @param {Field} etc (optional)
29351      * @return Column The column container object
29352      */
29353     column : function(c){
29354         var col = new Roo.form.Column(c);
29355         this.start(col);
29356         if(arguments.length > 1){ // duplicate code required because of Opera
29357             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29358             this.end();
29359         }
29360         return col;
29361     },
29362
29363     /**
29364      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29365      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29366      * until end() is called.
29367      * @param {Object} config The config to pass to the fieldset
29368      * @param {Field} field1 (optional)
29369      * @param {Field} field2 (optional)
29370      * @param {Field} etc (optional)
29371      * @return FieldSet The fieldset container object
29372      */
29373     fieldset : function(c){
29374         var fs = new Roo.form.FieldSet(c);
29375         this.start(fs);
29376         if(arguments.length > 1){ // duplicate code required because of Opera
29377             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29378             this.end();
29379         }
29380         return fs;
29381     },
29382
29383     /**
29384      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29385      * fields are added and the container is closed. If no fields are passed the container remains open
29386      * until end() is called.
29387      * @param {Object} config The config to pass to the Layout
29388      * @param {Field} field1 (optional)
29389      * @param {Field} field2 (optional)
29390      * @param {Field} etc (optional)
29391      * @return Layout The container object
29392      */
29393     container : function(c){
29394         var l = new Roo.form.Layout(c);
29395         this.start(l);
29396         if(arguments.length > 1){ // duplicate code required because of Opera
29397             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29398             this.end();
29399         }
29400         return l;
29401     },
29402
29403     /**
29404      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29405      * @param {Object} container A Roo.form.Layout or subclass of Layout
29406      * @return {Form} this
29407      */
29408     start : function(c){
29409         // cascade label info
29410         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29411         this.active.stack.push(c);
29412         c.ownerCt = this.active;
29413         this.active = c;
29414         return this;
29415     },
29416
29417     /**
29418      * Closes the current open container
29419      * @return {Form} this
29420      */
29421     end : function(){
29422         if(this.active == this.root){
29423             return this;
29424         }
29425         this.active = this.active.ownerCt;
29426         return this;
29427     },
29428
29429     /**
29430      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29431      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29432      * as the label of the field.
29433      * @param {Field} field1
29434      * @param {Field} field2 (optional)
29435      * @param {Field} etc. (optional)
29436      * @return {Form} this
29437      */
29438     add : function(){
29439         this.active.stack.push.apply(this.active.stack, arguments);
29440         this.allItems.push.apply(this.allItems,arguments);
29441         var r = [];
29442         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29443             if(a[i].isFormField){
29444                 r.push(a[i]);
29445             }
29446         }
29447         if(r.length > 0){
29448             Roo.form.Form.superclass.add.apply(this, r);
29449         }
29450         return this;
29451     },
29452     
29453
29454     
29455     
29456     
29457      /**
29458      * Find any element that has been added to a form, using it's ID or name
29459      * This can include framesets, columns etc. along with regular fields..
29460      * @param {String} id - id or name to find.
29461      
29462      * @return {Element} e - or false if nothing found.
29463      */
29464     findbyId : function(id)
29465     {
29466         var ret = false;
29467         if (!id) {
29468             return ret;
29469         }
29470         Roo.each(this.allItems, function(f){
29471             if (f.id == id || f.name == id ){
29472                 ret = f;
29473                 return false;
29474             }
29475         });
29476         return ret;
29477     },
29478
29479     
29480     
29481     /**
29482      * Render this form into the passed container. This should only be called once!
29483      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29484      * @return {Form} this
29485      */
29486     render : function(ct)
29487     {
29488         
29489         
29490         
29491         ct = Roo.get(ct);
29492         var o = this.autoCreate || {
29493             tag: 'form',
29494             method : this.method || 'POST',
29495             id : this.id || Roo.id()
29496         };
29497         this.initEl(ct.createChild(o));
29498
29499         this.root.render(this.el);
29500         
29501        
29502              
29503         this.items.each(function(f){
29504             f.render('x-form-el-'+f.id);
29505         });
29506
29507         if(this.buttons.length > 0){
29508             // tables are required to maintain order and for correct IE layout
29509             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29510                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29511                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29512             }}, null, true);
29513             var tr = tb.getElementsByTagName('tr')[0];
29514             for(var i = 0, len = this.buttons.length; i < len; i++) {
29515                 var b = this.buttons[i];
29516                 var td = document.createElement('td');
29517                 td.className = 'x-form-btn-td';
29518                 b.render(tr.appendChild(td));
29519             }
29520         }
29521         if(this.monitorValid){ // initialize after render
29522             this.startMonitoring();
29523         }
29524         this.fireEvent('rendered', this);
29525         return this;
29526     },
29527
29528     /**
29529      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29530      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29531      * object or a valid Roo.DomHelper element config
29532      * @param {Function} handler The function called when the button is clicked
29533      * @param {Object} scope (optional) The scope of the handler function
29534      * @return {Roo.Button}
29535      */
29536     addButton : function(config, handler, scope){
29537         var bc = {
29538             handler: handler,
29539             scope: scope,
29540             minWidth: this.minButtonWidth,
29541             hideParent:true
29542         };
29543         if(typeof config == "string"){
29544             bc.text = config;
29545         }else{
29546             Roo.apply(bc, config);
29547         }
29548         var btn = new Roo.Button(null, bc);
29549         this.buttons.push(btn);
29550         return btn;
29551     },
29552
29553      /**
29554      * Adds a series of form elements (using the xtype property as the factory method.
29555      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29556      * @param {Object} config 
29557      */
29558     
29559     addxtype : function()
29560     {
29561         var ar = Array.prototype.slice.call(arguments, 0);
29562         var ret = false;
29563         for(var i = 0; i < ar.length; i++) {
29564             if (!ar[i]) {
29565                 continue; // skip -- if this happends something invalid got sent, we 
29566                 // should ignore it, as basically that interface element will not show up
29567                 // and that should be pretty obvious!!
29568             }
29569             
29570             if (Roo.form[ar[i].xtype]) {
29571                 ar[i].form = this;
29572                 var fe = Roo.factory(ar[i], Roo.form);
29573                 if (!ret) {
29574                     ret = fe;
29575                 }
29576                 fe.form = this;
29577                 if (fe.store) {
29578                     fe.store.form = this;
29579                 }
29580                 if (fe.isLayout) {  
29581                          
29582                     this.start(fe);
29583                     this.allItems.push(fe);
29584                     if (fe.items && fe.addxtype) {
29585                         fe.addxtype.apply(fe, fe.items);
29586                         delete fe.items;
29587                     }
29588                      this.end();
29589                     continue;
29590                 }
29591                 
29592                 
29593                  
29594                 this.add(fe);
29595               //  console.log('adding ' + ar[i].xtype);
29596             }
29597             if (ar[i].xtype == 'Button') {  
29598                 //console.log('adding button');
29599                 //console.log(ar[i]);
29600                 this.addButton(ar[i]);
29601                 this.allItems.push(fe);
29602                 continue;
29603             }
29604             
29605             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29606                 alert('end is not supported on xtype any more, use items');
29607             //    this.end();
29608             //    //console.log('adding end');
29609             }
29610             
29611         }
29612         return ret;
29613     },
29614     
29615     /**
29616      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29617      * option "monitorValid"
29618      */
29619     startMonitoring : function(){
29620         if(!this.bound){
29621             this.bound = true;
29622             Roo.TaskMgr.start({
29623                 run : this.bindHandler,
29624                 interval : this.monitorPoll || 200,
29625                 scope: this
29626             });
29627         }
29628     },
29629
29630     /**
29631      * Stops monitoring of the valid state of this form
29632      */
29633     stopMonitoring : function(){
29634         this.bound = false;
29635     },
29636
29637     // private
29638     bindHandler : function(){
29639         if(!this.bound){
29640             return false; // stops binding
29641         }
29642         var valid = true;
29643         this.items.each(function(f){
29644             if(!f.isValid(true)){
29645                 valid = false;
29646                 return false;
29647             }
29648         });
29649         for(var i = 0, len = this.buttons.length; i < len; i++){
29650             var btn = this.buttons[i];
29651             if(btn.formBind === true && btn.disabled === valid){
29652                 btn.setDisabled(!valid);
29653             }
29654         }
29655         this.fireEvent('clientvalidation', this, valid);
29656     }
29657     
29658     
29659     
29660     
29661     
29662     
29663     
29664     
29665 });
29666
29667
29668 // back compat
29669 Roo.Form = Roo.form.Form;
29670 /*
29671  * Based on:
29672  * Ext JS Library 1.1.1
29673  * Copyright(c) 2006-2007, Ext JS, LLC.
29674  *
29675  * Originally Released Under LGPL - original licence link has changed is not relivant.
29676  *
29677  * Fork - LGPL
29678  * <script type="text/javascript">
29679  */
29680
29681 // as we use this in bootstrap.
29682 Roo.namespace('Roo.form');
29683  /**
29684  * @class Roo.form.Action
29685  * Internal Class used to handle form actions
29686  * @constructor
29687  * @param {Roo.form.BasicForm} el The form element or its id
29688  * @param {Object} config Configuration options
29689  */
29690
29691  
29692  
29693 // define the action interface
29694 Roo.form.Action = function(form, options){
29695     this.form = form;
29696     this.options = options || {};
29697 };
29698 /**
29699  * Client Validation Failed
29700  * @const 
29701  */
29702 Roo.form.Action.CLIENT_INVALID = 'client';
29703 /**
29704  * Server Validation Failed
29705  * @const 
29706  */
29707 Roo.form.Action.SERVER_INVALID = 'server';
29708  /**
29709  * Connect to Server Failed
29710  * @const 
29711  */
29712 Roo.form.Action.CONNECT_FAILURE = 'connect';
29713 /**
29714  * Reading Data from Server Failed
29715  * @const 
29716  */
29717 Roo.form.Action.LOAD_FAILURE = 'load';
29718
29719 Roo.form.Action.prototype = {
29720     type : 'default',
29721     failureType : undefined,
29722     response : undefined,
29723     result : undefined,
29724
29725     // interface method
29726     run : function(options){
29727
29728     },
29729
29730     // interface method
29731     success : function(response){
29732
29733     },
29734
29735     // interface method
29736     handleResponse : function(response){
29737
29738     },
29739
29740     // default connection failure
29741     failure : function(response){
29742         
29743         this.response = response;
29744         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29745         this.form.afterAction(this, false);
29746     },
29747
29748     processResponse : function(response){
29749         this.response = response;
29750         if(!response.responseText){
29751             return true;
29752         }
29753         this.result = this.handleResponse(response);
29754         return this.result;
29755     },
29756
29757     // utility functions used internally
29758     getUrl : function(appendParams){
29759         var url = this.options.url || this.form.url || this.form.el.dom.action;
29760         if(appendParams){
29761             var p = this.getParams();
29762             if(p){
29763                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29764             }
29765         }
29766         return url;
29767     },
29768
29769     getMethod : function(){
29770         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29771     },
29772
29773     getParams : function(){
29774         var bp = this.form.baseParams;
29775         var p = this.options.params;
29776         if(p){
29777             if(typeof p == "object"){
29778                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29779             }else if(typeof p == 'string' && bp){
29780                 p += '&' + Roo.urlEncode(bp);
29781             }
29782         }else if(bp){
29783             p = Roo.urlEncode(bp);
29784         }
29785         return p;
29786     },
29787
29788     createCallback : function(){
29789         return {
29790             success: this.success,
29791             failure: this.failure,
29792             scope: this,
29793             timeout: (this.form.timeout*1000),
29794             upload: this.form.fileUpload ? this.success : undefined
29795         };
29796     }
29797 };
29798
29799 Roo.form.Action.Submit = function(form, options){
29800     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29801 };
29802
29803 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29804     type : 'submit',
29805
29806     haveProgress : false,
29807     uploadComplete : false,
29808     
29809     // uploadProgress indicator.
29810     uploadProgress : function()
29811     {
29812         if (!this.form.progressUrl) {
29813             return;
29814         }
29815         
29816         if (!this.haveProgress) {
29817             Roo.MessageBox.progress("Uploading", "Uploading");
29818         }
29819         if (this.uploadComplete) {
29820            Roo.MessageBox.hide();
29821            return;
29822         }
29823         
29824         this.haveProgress = true;
29825    
29826         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29827         
29828         var c = new Roo.data.Connection();
29829         c.request({
29830             url : this.form.progressUrl,
29831             params: {
29832                 id : uid
29833             },
29834             method: 'GET',
29835             success : function(req){
29836                //console.log(data);
29837                 var rdata = false;
29838                 var edata;
29839                 try  {
29840                    rdata = Roo.decode(req.responseText)
29841                 } catch (e) {
29842                     Roo.log("Invalid data from server..");
29843                     Roo.log(edata);
29844                     return;
29845                 }
29846                 if (!rdata || !rdata.success) {
29847                     Roo.log(rdata);
29848                     Roo.MessageBox.alert(Roo.encode(rdata));
29849                     return;
29850                 }
29851                 var data = rdata.data;
29852                 
29853                 if (this.uploadComplete) {
29854                    Roo.MessageBox.hide();
29855                    return;
29856                 }
29857                    
29858                 if (data){
29859                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29860                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29861                     );
29862                 }
29863                 this.uploadProgress.defer(2000,this);
29864             },
29865        
29866             failure: function(data) {
29867                 Roo.log('progress url failed ');
29868                 Roo.log(data);
29869             },
29870             scope : this
29871         });
29872            
29873     },
29874     
29875     
29876     run : function()
29877     {
29878         // run get Values on the form, so it syncs any secondary forms.
29879         this.form.getValues();
29880         
29881         var o = this.options;
29882         var method = this.getMethod();
29883         var isPost = method == 'POST';
29884         if(o.clientValidation === false || this.form.isValid()){
29885             
29886             if (this.form.progressUrl) {
29887                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29888                     (new Date() * 1) + '' + Math.random());
29889                     
29890             } 
29891             
29892             
29893             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29894                 form:this.form.el.dom,
29895                 url:this.getUrl(!isPost),
29896                 method: method,
29897                 params:isPost ? this.getParams() : null,
29898                 isUpload: this.form.fileUpload
29899             }));
29900             
29901             this.uploadProgress();
29902
29903         }else if (o.clientValidation !== false){ // client validation failed
29904             this.failureType = Roo.form.Action.CLIENT_INVALID;
29905             this.form.afterAction(this, false);
29906         }
29907     },
29908
29909     success : function(response)
29910     {
29911         this.uploadComplete= true;
29912         if (this.haveProgress) {
29913             Roo.MessageBox.hide();
29914         }
29915         
29916         
29917         var result = this.processResponse(response);
29918         if(result === true || result.success){
29919             this.form.afterAction(this, true);
29920             return;
29921         }
29922         if(result.errors){
29923             this.form.markInvalid(result.errors);
29924             this.failureType = Roo.form.Action.SERVER_INVALID;
29925         }
29926         this.form.afterAction(this, false);
29927     },
29928     failure : function(response)
29929     {
29930         this.uploadComplete= true;
29931         if (this.haveProgress) {
29932             Roo.MessageBox.hide();
29933         }
29934         
29935         this.response = response;
29936         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29937         this.form.afterAction(this, false);
29938     },
29939     
29940     handleResponse : function(response){
29941         if(this.form.errorReader){
29942             var rs = this.form.errorReader.read(response);
29943             var errors = [];
29944             if(rs.records){
29945                 for(var i = 0, len = rs.records.length; i < len; i++) {
29946                     var r = rs.records[i];
29947                     errors[i] = r.data;
29948                 }
29949             }
29950             if(errors.length < 1){
29951                 errors = null;
29952             }
29953             return {
29954                 success : rs.success,
29955                 errors : errors
29956             };
29957         }
29958         var ret = false;
29959         try {
29960             ret = Roo.decode(response.responseText);
29961         } catch (e) {
29962             ret = {
29963                 success: false,
29964                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29965                 errors : []
29966             };
29967         }
29968         return ret;
29969         
29970     }
29971 });
29972
29973
29974 Roo.form.Action.Load = function(form, options){
29975     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29976     this.reader = this.form.reader;
29977 };
29978
29979 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29980     type : 'load',
29981
29982     run : function(){
29983         
29984         Roo.Ajax.request(Roo.apply(
29985                 this.createCallback(), {
29986                     method:this.getMethod(),
29987                     url:this.getUrl(false),
29988                     params:this.getParams()
29989         }));
29990     },
29991
29992     success : function(response){
29993         
29994         var result = this.processResponse(response);
29995         if(result === true || !result.success || !result.data){
29996             this.failureType = Roo.form.Action.LOAD_FAILURE;
29997             this.form.afterAction(this, false);
29998             return;
29999         }
30000         this.form.clearInvalid();
30001         this.form.setValues(result.data);
30002         this.form.afterAction(this, true);
30003     },
30004
30005     handleResponse : function(response){
30006         if(this.form.reader){
30007             var rs = this.form.reader.read(response);
30008             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30009             return {
30010                 success : rs.success,
30011                 data : data
30012             };
30013         }
30014         return Roo.decode(response.responseText);
30015     }
30016 });
30017
30018 Roo.form.Action.ACTION_TYPES = {
30019     'load' : Roo.form.Action.Load,
30020     'submit' : Roo.form.Action.Submit
30021 };/*
30022  * Based on:
30023  * Ext JS Library 1.1.1
30024  * Copyright(c) 2006-2007, Ext JS, LLC.
30025  *
30026  * Originally Released Under LGPL - original licence link has changed is not relivant.
30027  *
30028  * Fork - LGPL
30029  * <script type="text/javascript">
30030  */
30031  
30032 /**
30033  * @class Roo.form.Layout
30034  * @extends Roo.Component
30035  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30036  * @constructor
30037  * @param {Object} config Configuration options
30038  */
30039 Roo.form.Layout = function(config){
30040     var xitems = [];
30041     if (config.items) {
30042         xitems = config.items;
30043         delete config.items;
30044     }
30045     Roo.form.Layout.superclass.constructor.call(this, config);
30046     this.stack = [];
30047     Roo.each(xitems, this.addxtype, this);
30048      
30049 };
30050
30051 Roo.extend(Roo.form.Layout, Roo.Component, {
30052     /**
30053      * @cfg {String/Object} autoCreate
30054      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30055      */
30056     /**
30057      * @cfg {String/Object/Function} style
30058      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30059      * a function which returns such a specification.
30060      */
30061     /**
30062      * @cfg {String} labelAlign
30063      * Valid values are "left," "top" and "right" (defaults to "left")
30064      */
30065     /**
30066      * @cfg {Number} labelWidth
30067      * Fixed width in pixels of all field labels (defaults to undefined)
30068      */
30069     /**
30070      * @cfg {Boolean} clear
30071      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30072      */
30073     clear : true,
30074     /**
30075      * @cfg {String} labelSeparator
30076      * The separator to use after field labels (defaults to ':')
30077      */
30078     labelSeparator : ':',
30079     /**
30080      * @cfg {Boolean} hideLabels
30081      * True to suppress the display of field labels in this layout (defaults to false)
30082      */
30083     hideLabels : false,
30084
30085     // private
30086     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30087     
30088     isLayout : true,
30089     
30090     // private
30091     onRender : function(ct, position){
30092         if(this.el){ // from markup
30093             this.el = Roo.get(this.el);
30094         }else {  // generate
30095             var cfg = this.getAutoCreate();
30096             this.el = ct.createChild(cfg, position);
30097         }
30098         if(this.style){
30099             this.el.applyStyles(this.style);
30100         }
30101         if(this.labelAlign){
30102             this.el.addClass('x-form-label-'+this.labelAlign);
30103         }
30104         if(this.hideLabels){
30105             this.labelStyle = "display:none";
30106             this.elementStyle = "padding-left:0;";
30107         }else{
30108             if(typeof this.labelWidth == 'number'){
30109                 this.labelStyle = "width:"+this.labelWidth+"px;";
30110                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30111             }
30112             if(this.labelAlign == 'top'){
30113                 this.labelStyle = "width:auto;";
30114                 this.elementStyle = "padding-left:0;";
30115             }
30116         }
30117         var stack = this.stack;
30118         var slen = stack.length;
30119         if(slen > 0){
30120             if(!this.fieldTpl){
30121                 var t = new Roo.Template(
30122                     '<div class="x-form-item {5}">',
30123                         '<label for="{0}" style="{2}">{1}{4}</label>',
30124                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30125                         '</div>',
30126                     '</div><div class="x-form-clear-left"></div>'
30127                 );
30128                 t.disableFormats = true;
30129                 t.compile();
30130                 Roo.form.Layout.prototype.fieldTpl = t;
30131             }
30132             for(var i = 0; i < slen; i++) {
30133                 if(stack[i].isFormField){
30134                     this.renderField(stack[i]);
30135                 }else{
30136                     this.renderComponent(stack[i]);
30137                 }
30138             }
30139         }
30140         if(this.clear){
30141             this.el.createChild({cls:'x-form-clear'});
30142         }
30143     },
30144
30145     // private
30146     renderField : function(f){
30147         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30148                f.id, //0
30149                f.fieldLabel, //1
30150                f.labelStyle||this.labelStyle||'', //2
30151                this.elementStyle||'', //3
30152                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30153                f.itemCls||this.itemCls||''  //5
30154        ], true).getPrevSibling());
30155     },
30156
30157     // private
30158     renderComponent : function(c){
30159         c.render(c.isLayout ? this.el : this.el.createChild());    
30160     },
30161     /**
30162      * Adds a object form elements (using the xtype property as the factory method.)
30163      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30164      * @param {Object} config 
30165      */
30166     addxtype : function(o)
30167     {
30168         // create the lement.
30169         o.form = this.form;
30170         var fe = Roo.factory(o, Roo.form);
30171         this.form.allItems.push(fe);
30172         this.stack.push(fe);
30173         
30174         if (fe.isFormField) {
30175             this.form.items.add(fe);
30176         }
30177          
30178         return fe;
30179     }
30180 });
30181
30182 /**
30183  * @class Roo.form.Column
30184  * @extends Roo.form.Layout
30185  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30186  * @constructor
30187  * @param {Object} config Configuration options
30188  */
30189 Roo.form.Column = function(config){
30190     Roo.form.Column.superclass.constructor.call(this, config);
30191 };
30192
30193 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30194     /**
30195      * @cfg {Number/String} width
30196      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30197      */
30198     /**
30199      * @cfg {String/Object} autoCreate
30200      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30201      */
30202
30203     // private
30204     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30205
30206     // private
30207     onRender : function(ct, position){
30208         Roo.form.Column.superclass.onRender.call(this, ct, position);
30209         if(this.width){
30210             this.el.setWidth(this.width);
30211         }
30212     }
30213 });
30214
30215
30216 /**
30217  * @class Roo.form.Row
30218  * @extends Roo.form.Layout
30219  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30220  * @constructor
30221  * @param {Object} config Configuration options
30222  */
30223
30224  
30225 Roo.form.Row = function(config){
30226     Roo.form.Row.superclass.constructor.call(this, config);
30227 };
30228  
30229 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30230       /**
30231      * @cfg {Number/String} width
30232      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30233      */
30234     /**
30235      * @cfg {Number/String} height
30236      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30237      */
30238     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30239     
30240     padWidth : 20,
30241     // private
30242     onRender : function(ct, position){
30243         //console.log('row render');
30244         if(!this.rowTpl){
30245             var t = new Roo.Template(
30246                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30247                     '<label for="{0}" style="{2}">{1}{4}</label>',
30248                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30249                     '</div>',
30250                 '</div>'
30251             );
30252             t.disableFormats = true;
30253             t.compile();
30254             Roo.form.Layout.prototype.rowTpl = t;
30255         }
30256         this.fieldTpl = this.rowTpl;
30257         
30258         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30259         var labelWidth = 100;
30260         
30261         if ((this.labelAlign != 'top')) {
30262             if (typeof this.labelWidth == 'number') {
30263                 labelWidth = this.labelWidth
30264             }
30265             this.padWidth =  20 + labelWidth;
30266             
30267         }
30268         
30269         Roo.form.Column.superclass.onRender.call(this, ct, position);
30270         if(this.width){
30271             this.el.setWidth(this.width);
30272         }
30273         if(this.height){
30274             this.el.setHeight(this.height);
30275         }
30276     },
30277     
30278     // private
30279     renderField : function(f){
30280         f.fieldEl = this.fieldTpl.append(this.el, [
30281                f.id, f.fieldLabel,
30282                f.labelStyle||this.labelStyle||'',
30283                this.elementStyle||'',
30284                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30285                f.itemCls||this.itemCls||'',
30286                f.width ? f.width + this.padWidth : 160 + this.padWidth
30287        ],true);
30288     }
30289 });
30290  
30291
30292 /**
30293  * @class Roo.form.FieldSet
30294  * @extends Roo.form.Layout
30295  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30296  * @constructor
30297  * @param {Object} config Configuration options
30298  */
30299 Roo.form.FieldSet = function(config){
30300     Roo.form.FieldSet.superclass.constructor.call(this, config);
30301 };
30302
30303 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30304     /**
30305      * @cfg {String} legend
30306      * The text to display as the legend for the FieldSet (defaults to '')
30307      */
30308     /**
30309      * @cfg {String/Object} autoCreate
30310      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30311      */
30312
30313     // private
30314     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30315
30316     // private
30317     onRender : function(ct, position){
30318         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30319         if(this.legend){
30320             this.setLegend(this.legend);
30321         }
30322     },
30323
30324     // private
30325     setLegend : function(text){
30326         if(this.rendered){
30327             this.el.child('legend').update(text);
30328         }
30329     }
30330 });/*
30331  * Based on:
30332  * Ext JS Library 1.1.1
30333  * Copyright(c) 2006-2007, Ext JS, LLC.
30334  *
30335  * Originally Released Under LGPL - original licence link has changed is not relivant.
30336  *
30337  * Fork - LGPL
30338  * <script type="text/javascript">
30339  */
30340 /**
30341  * @class Roo.form.VTypes
30342  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30343  * @singleton
30344  */
30345 Roo.form.VTypes = function(){
30346     // closure these in so they are only created once.
30347     var alpha = /^[a-zA-Z_]+$/;
30348     var alphanum = /^[a-zA-Z0-9_]+$/;
30349     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30350     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30351
30352     // All these messages and functions are configurable
30353     return {
30354         /**
30355          * The function used to validate email addresses
30356          * @param {String} value The email address
30357          */
30358         'email' : function(v){
30359             return email.test(v);
30360         },
30361         /**
30362          * The error text to display when the email validation function returns false
30363          * @type String
30364          */
30365         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30366         /**
30367          * The keystroke filter mask to be applied on email input
30368          * @type RegExp
30369          */
30370         'emailMask' : /[a-z0-9_\.\-@]/i,
30371
30372         /**
30373          * The function used to validate URLs
30374          * @param {String} value The URL
30375          */
30376         'url' : function(v){
30377             return url.test(v);
30378         },
30379         /**
30380          * The error text to display when the url validation function returns false
30381          * @type String
30382          */
30383         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30384         
30385         /**
30386          * The function used to validate alpha values
30387          * @param {String} value The value
30388          */
30389         'alpha' : function(v){
30390             return alpha.test(v);
30391         },
30392         /**
30393          * The error text to display when the alpha validation function returns false
30394          * @type String
30395          */
30396         'alphaText' : 'This field should only contain letters and _',
30397         /**
30398          * The keystroke filter mask to be applied on alpha input
30399          * @type RegExp
30400          */
30401         'alphaMask' : /[a-z_]/i,
30402
30403         /**
30404          * The function used to validate alphanumeric values
30405          * @param {String} value The value
30406          */
30407         'alphanum' : function(v){
30408             return alphanum.test(v);
30409         },
30410         /**
30411          * The error text to display when the alphanumeric validation function returns false
30412          * @type String
30413          */
30414         'alphanumText' : 'This field should only contain letters, numbers and _',
30415         /**
30416          * The keystroke filter mask to be applied on alphanumeric input
30417          * @type RegExp
30418          */
30419         'alphanumMask' : /[a-z0-9_]/i
30420     };
30421 }();//<script type="text/javascript">
30422
30423 /**
30424  * @class Roo.form.FCKeditor
30425  * @extends Roo.form.TextArea
30426  * Wrapper around the FCKEditor http://www.fckeditor.net
30427  * @constructor
30428  * Creates a new FCKeditor
30429  * @param {Object} config Configuration options
30430  */
30431 Roo.form.FCKeditor = function(config){
30432     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30433     this.addEvents({
30434          /**
30435          * @event editorinit
30436          * Fired when the editor is initialized - you can add extra handlers here..
30437          * @param {FCKeditor} this
30438          * @param {Object} the FCK object.
30439          */
30440         editorinit : true
30441     });
30442     
30443     
30444 };
30445 Roo.form.FCKeditor.editors = { };
30446 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30447 {
30448     //defaultAutoCreate : {
30449     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30450     //},
30451     // private
30452     /**
30453      * @cfg {Object} fck options - see fck manual for details.
30454      */
30455     fckconfig : false,
30456     
30457     /**
30458      * @cfg {Object} fck toolbar set (Basic or Default)
30459      */
30460     toolbarSet : 'Basic',
30461     /**
30462      * @cfg {Object} fck BasePath
30463      */ 
30464     basePath : '/fckeditor/',
30465     
30466     
30467     frame : false,
30468     
30469     value : '',
30470     
30471    
30472     onRender : function(ct, position)
30473     {
30474         if(!this.el){
30475             this.defaultAutoCreate = {
30476                 tag: "textarea",
30477                 style:"width:300px;height:60px;",
30478                 autocomplete: "new-password"
30479             };
30480         }
30481         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30482         /*
30483         if(this.grow){
30484             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30485             if(this.preventScrollbars){
30486                 this.el.setStyle("overflow", "hidden");
30487             }
30488             this.el.setHeight(this.growMin);
30489         }
30490         */
30491         //console.log('onrender' + this.getId() );
30492         Roo.form.FCKeditor.editors[this.getId()] = this;
30493          
30494
30495         this.replaceTextarea() ;
30496         
30497     },
30498     
30499     getEditor : function() {
30500         return this.fckEditor;
30501     },
30502     /**
30503      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30504      * @param {Mixed} value The value to set
30505      */
30506     
30507     
30508     setValue : function(value)
30509     {
30510         //console.log('setValue: ' + value);
30511         
30512         if(typeof(value) == 'undefined') { // not sure why this is happending...
30513             return;
30514         }
30515         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30516         
30517         //if(!this.el || !this.getEditor()) {
30518         //    this.value = value;
30519             //this.setValue.defer(100,this,[value]);    
30520         //    return;
30521         //} 
30522         
30523         if(!this.getEditor()) {
30524             return;
30525         }
30526         
30527         this.getEditor().SetData(value);
30528         
30529         //
30530
30531     },
30532
30533     /**
30534      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30535      * @return {Mixed} value The field value
30536      */
30537     getValue : function()
30538     {
30539         
30540         if (this.frame && this.frame.dom.style.display == 'none') {
30541             return Roo.form.FCKeditor.superclass.getValue.call(this);
30542         }
30543         
30544         if(!this.el || !this.getEditor()) {
30545            
30546            // this.getValue.defer(100,this); 
30547             return this.value;
30548         }
30549        
30550         
30551         var value=this.getEditor().GetData();
30552         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30553         return Roo.form.FCKeditor.superclass.getValue.call(this);
30554         
30555
30556     },
30557
30558     /**
30559      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30560      * @return {Mixed} value The field value
30561      */
30562     getRawValue : function()
30563     {
30564         if (this.frame && this.frame.dom.style.display == 'none') {
30565             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30566         }
30567         
30568         if(!this.el || !this.getEditor()) {
30569             //this.getRawValue.defer(100,this); 
30570             return this.value;
30571             return;
30572         }
30573         
30574         
30575         
30576         var value=this.getEditor().GetData();
30577         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30578         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30579          
30580     },
30581     
30582     setSize : function(w,h) {
30583         
30584         
30585         
30586         //if (this.frame && this.frame.dom.style.display == 'none') {
30587         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30588         //    return;
30589         //}
30590         //if(!this.el || !this.getEditor()) {
30591         //    this.setSize.defer(100,this, [w,h]); 
30592         //    return;
30593         //}
30594         
30595         
30596         
30597         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30598         
30599         this.frame.dom.setAttribute('width', w);
30600         this.frame.dom.setAttribute('height', h);
30601         this.frame.setSize(w,h);
30602         
30603     },
30604     
30605     toggleSourceEdit : function(value) {
30606         
30607       
30608          
30609         this.el.dom.style.display = value ? '' : 'none';
30610         this.frame.dom.style.display = value ?  'none' : '';
30611         
30612     },
30613     
30614     
30615     focus: function(tag)
30616     {
30617         if (this.frame.dom.style.display == 'none') {
30618             return Roo.form.FCKeditor.superclass.focus.call(this);
30619         }
30620         if(!this.el || !this.getEditor()) {
30621             this.focus.defer(100,this, [tag]); 
30622             return;
30623         }
30624         
30625         
30626         
30627         
30628         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30629         this.getEditor().Focus();
30630         if (tgs.length) {
30631             if (!this.getEditor().Selection.GetSelection()) {
30632                 this.focus.defer(100,this, [tag]); 
30633                 return;
30634             }
30635             
30636             
30637             var r = this.getEditor().EditorDocument.createRange();
30638             r.setStart(tgs[0],0);
30639             r.setEnd(tgs[0],0);
30640             this.getEditor().Selection.GetSelection().removeAllRanges();
30641             this.getEditor().Selection.GetSelection().addRange(r);
30642             this.getEditor().Focus();
30643         }
30644         
30645     },
30646     
30647     
30648     
30649     replaceTextarea : function()
30650     {
30651         if ( document.getElementById( this.getId() + '___Frame' ) ) {
30652             return ;
30653         }
30654         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30655         //{
30656             // We must check the elements firstly using the Id and then the name.
30657         var oTextarea = document.getElementById( this.getId() );
30658         
30659         var colElementsByName = document.getElementsByName( this.getId() ) ;
30660          
30661         oTextarea.style.display = 'none' ;
30662
30663         if ( oTextarea.tabIndex ) {            
30664             this.TabIndex = oTextarea.tabIndex ;
30665         }
30666         
30667         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30668         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30669         this.frame = Roo.get(this.getId() + '___Frame')
30670     },
30671     
30672     _getConfigHtml : function()
30673     {
30674         var sConfig = '' ;
30675
30676         for ( var o in this.fckconfig ) {
30677             sConfig += sConfig.length > 0  ? '&amp;' : '';
30678             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30679         }
30680
30681         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30682     },
30683     
30684     
30685     _getIFrameHtml : function()
30686     {
30687         var sFile = 'fckeditor.html' ;
30688         /* no idea what this is about..
30689         try
30690         {
30691             if ( (/fcksource=true/i).test( window.top.location.search ) )
30692                 sFile = 'fckeditor.original.html' ;
30693         }
30694         catch (e) { 
30695         */
30696
30697         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30698         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30699         
30700         
30701         var html = '<iframe id="' + this.getId() +
30702             '___Frame" src="' + sLink +
30703             '" width="' + this.width +
30704             '" height="' + this.height + '"' +
30705             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30706             ' frameborder="0" scrolling="no"></iframe>' ;
30707
30708         return html ;
30709     },
30710     
30711     _insertHtmlBefore : function( html, element )
30712     {
30713         if ( element.insertAdjacentHTML )       {
30714             // IE
30715             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30716         } else { // Gecko
30717             var oRange = document.createRange() ;
30718             oRange.setStartBefore( element ) ;
30719             var oFragment = oRange.createContextualFragment( html );
30720             element.parentNode.insertBefore( oFragment, element ) ;
30721         }
30722     }
30723     
30724     
30725   
30726     
30727     
30728     
30729     
30730
30731 });
30732
30733 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30734
30735 function FCKeditor_OnComplete(editorInstance){
30736     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30737     f.fckEditor = editorInstance;
30738     //console.log("loaded");
30739     f.fireEvent('editorinit', f, editorInstance);
30740
30741   
30742
30743  
30744
30745
30746
30747
30748
30749
30750
30751
30752
30753
30754
30755
30756
30757
30758
30759 //<script type="text/javascript">
30760 /**
30761  * @class Roo.form.GridField
30762  * @extends Roo.form.Field
30763  * Embed a grid (or editable grid into a form)
30764  * STATUS ALPHA
30765  * 
30766  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30767  * it needs 
30768  * xgrid.store = Roo.data.Store
30769  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30770  * xgrid.store.reader = Roo.data.JsonReader 
30771  * 
30772  * 
30773  * @constructor
30774  * Creates a new GridField
30775  * @param {Object} config Configuration options
30776  */
30777 Roo.form.GridField = function(config){
30778     Roo.form.GridField.superclass.constructor.call(this, config);
30779      
30780 };
30781
30782 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30783     /**
30784      * @cfg {Number} width  - used to restrict width of grid..
30785      */
30786     width : 100,
30787     /**
30788      * @cfg {Number} height - used to restrict height of grid..
30789      */
30790     height : 50,
30791      /**
30792      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30793          * 
30794          *}
30795      */
30796     xgrid : false, 
30797     /**
30798      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30799      * {tag: "input", type: "checkbox", autocomplete: "off"})
30800      */
30801    // defaultAutoCreate : { tag: 'div' },
30802     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30803     /**
30804      * @cfg {String} addTitle Text to include for adding a title.
30805      */
30806     addTitle : false,
30807     //
30808     onResize : function(){
30809         Roo.form.Field.superclass.onResize.apply(this, arguments);
30810     },
30811
30812     initEvents : function(){
30813         // Roo.form.Checkbox.superclass.initEvents.call(this);
30814         // has no events...
30815        
30816     },
30817
30818
30819     getResizeEl : function(){
30820         return this.wrap;
30821     },
30822
30823     getPositionEl : function(){
30824         return this.wrap;
30825     },
30826
30827     // private
30828     onRender : function(ct, position){
30829         
30830         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30831         var style = this.style;
30832         delete this.style;
30833         
30834         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30835         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30836         this.viewEl = this.wrap.createChild({ tag: 'div' });
30837         if (style) {
30838             this.viewEl.applyStyles(style);
30839         }
30840         if (this.width) {
30841             this.viewEl.setWidth(this.width);
30842         }
30843         if (this.height) {
30844             this.viewEl.setHeight(this.height);
30845         }
30846         //if(this.inputValue !== undefined){
30847         //this.setValue(this.value);
30848         
30849         
30850         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30851         
30852         
30853         this.grid.render();
30854         this.grid.getDataSource().on('remove', this.refreshValue, this);
30855         this.grid.getDataSource().on('update', this.refreshValue, this);
30856         this.grid.on('afteredit', this.refreshValue, this);
30857  
30858     },
30859      
30860     
30861     /**
30862      * Sets the value of the item. 
30863      * @param {String} either an object  or a string..
30864      */
30865     setValue : function(v){
30866         //this.value = v;
30867         v = v || []; // empty set..
30868         // this does not seem smart - it really only affects memoryproxy grids..
30869         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30870             var ds = this.grid.getDataSource();
30871             // assumes a json reader..
30872             var data = {}
30873             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30874             ds.loadData( data);
30875         }
30876         // clear selection so it does not get stale.
30877         if (this.grid.sm) { 
30878             this.grid.sm.clearSelections();
30879         }
30880         
30881         Roo.form.GridField.superclass.setValue.call(this, v);
30882         this.refreshValue();
30883         // should load data in the grid really....
30884     },
30885     
30886     // private
30887     refreshValue: function() {
30888          var val = [];
30889         this.grid.getDataSource().each(function(r) {
30890             val.push(r.data);
30891         });
30892         this.el.dom.value = Roo.encode(val);
30893     }
30894     
30895      
30896     
30897     
30898 });/*
30899  * Based on:
30900  * Ext JS Library 1.1.1
30901  * Copyright(c) 2006-2007, Ext JS, LLC.
30902  *
30903  * Originally Released Under LGPL - original licence link has changed is not relivant.
30904  *
30905  * Fork - LGPL
30906  * <script type="text/javascript">
30907  */
30908 /**
30909  * @class Roo.form.DisplayField
30910  * @extends Roo.form.Field
30911  * A generic Field to display non-editable data.
30912  * @cfg {Boolean} closable (true|false) default false
30913  * @constructor
30914  * Creates a new Display Field item.
30915  * @param {Object} config Configuration options
30916  */
30917 Roo.form.DisplayField = function(config){
30918     Roo.form.DisplayField.superclass.constructor.call(this, config);
30919     
30920     this.addEvents({
30921         /**
30922          * @event close
30923          * Fires after the click the close btn
30924              * @param {Roo.form.DisplayField} this
30925              */
30926         close : true
30927     });
30928 };
30929
30930 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30931     inputType:      'hidden',
30932     allowBlank:     true,
30933     readOnly:         true,
30934     
30935  
30936     /**
30937      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30938      */
30939     focusClass : undefined,
30940     /**
30941      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30942      */
30943     fieldClass: 'x-form-field',
30944     
30945      /**
30946      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30947      */
30948     valueRenderer: undefined,
30949     
30950     width: 100,
30951     /**
30952      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30953      * {tag: "input", type: "checkbox", autocomplete: "off"})
30954      */
30955      
30956  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30957  
30958     closable : false,
30959     
30960     onResize : function(){
30961         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30962         
30963     },
30964
30965     initEvents : function(){
30966         // Roo.form.Checkbox.superclass.initEvents.call(this);
30967         // has no events...
30968         
30969         if(this.closable){
30970             this.closeEl.on('click', this.onClose, this);
30971         }
30972        
30973     },
30974
30975
30976     getResizeEl : function(){
30977         return this.wrap;
30978     },
30979
30980     getPositionEl : function(){
30981         return this.wrap;
30982     },
30983
30984     // private
30985     onRender : function(ct, position){
30986         
30987         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30988         //if(this.inputValue !== undefined){
30989         this.wrap = this.el.wrap();
30990         
30991         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30992         
30993         if(this.closable){
30994             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
30995         }
30996         
30997         if (this.bodyStyle) {
30998             this.viewEl.applyStyles(this.bodyStyle);
30999         }
31000         //this.viewEl.setStyle('padding', '2px');
31001         
31002         this.setValue(this.value);
31003         
31004     },
31005 /*
31006     // private
31007     initValue : Roo.emptyFn,
31008
31009   */
31010
31011         // private
31012     onClick : function(){
31013         
31014     },
31015
31016     /**
31017      * Sets the checked state of the checkbox.
31018      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31019      */
31020     setValue : function(v){
31021         this.value = v;
31022         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31023         // this might be called before we have a dom element..
31024         if (!this.viewEl) {
31025             return;
31026         }
31027         this.viewEl.dom.innerHTML = html;
31028         Roo.form.DisplayField.superclass.setValue.call(this, v);
31029
31030     },
31031     
31032     onClose : function(e)
31033     {
31034         e.preventDefault();
31035         
31036         this.fireEvent('close', this);
31037     }
31038 });/*
31039  * 
31040  * Licence- LGPL
31041  * 
31042  */
31043
31044 /**
31045  * @class Roo.form.DayPicker
31046  * @extends Roo.form.Field
31047  * A Day picker show [M] [T] [W] ....
31048  * @constructor
31049  * Creates a new Day Picker
31050  * @param {Object} config Configuration options
31051  */
31052 Roo.form.DayPicker= function(config){
31053     Roo.form.DayPicker.superclass.constructor.call(this, config);
31054      
31055 };
31056
31057 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31058     /**
31059      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31060      */
31061     focusClass : undefined,
31062     /**
31063      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31064      */
31065     fieldClass: "x-form-field",
31066    
31067     /**
31068      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31069      * {tag: "input", type: "checkbox", autocomplete: "off"})
31070      */
31071     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31072     
31073    
31074     actionMode : 'viewEl', 
31075     //
31076     // private
31077  
31078     inputType : 'hidden',
31079     
31080      
31081     inputElement: false, // real input element?
31082     basedOn: false, // ????
31083     
31084     isFormField: true, // not sure where this is needed!!!!
31085
31086     onResize : function(){
31087         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31088         if(!this.boxLabel){
31089             this.el.alignTo(this.wrap, 'c-c');
31090         }
31091     },
31092
31093     initEvents : function(){
31094         Roo.form.Checkbox.superclass.initEvents.call(this);
31095         this.el.on("click", this.onClick,  this);
31096         this.el.on("change", this.onClick,  this);
31097     },
31098
31099
31100     getResizeEl : function(){
31101         return this.wrap;
31102     },
31103
31104     getPositionEl : function(){
31105         return this.wrap;
31106     },
31107
31108     
31109     // private
31110     onRender : function(ct, position){
31111         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31112        
31113         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31114         
31115         var r1 = '<table><tr>';
31116         var r2 = '<tr class="x-form-daypick-icons">';
31117         for (var i=0; i < 7; i++) {
31118             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31119             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31120         }
31121         
31122         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31123         viewEl.select('img').on('click', this.onClick, this);
31124         this.viewEl = viewEl;   
31125         
31126         
31127         // this will not work on Chrome!!!
31128         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31129         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31130         
31131         
31132           
31133
31134     },
31135
31136     // private
31137     initValue : Roo.emptyFn,
31138
31139     /**
31140      * Returns the checked state of the checkbox.
31141      * @return {Boolean} True if checked, else false
31142      */
31143     getValue : function(){
31144         return this.el.dom.value;
31145         
31146     },
31147
31148         // private
31149     onClick : function(e){ 
31150         //this.setChecked(!this.checked);
31151         Roo.get(e.target).toggleClass('x-menu-item-checked');
31152         this.refreshValue();
31153         //if(this.el.dom.checked != this.checked){
31154         //    this.setValue(this.el.dom.checked);
31155        // }
31156     },
31157     
31158     // private
31159     refreshValue : function()
31160     {
31161         var val = '';
31162         this.viewEl.select('img',true).each(function(e,i,n)  {
31163             val += e.is(".x-menu-item-checked") ? String(n) : '';
31164         });
31165         this.setValue(val, true);
31166     },
31167
31168     /**
31169      * Sets the checked state of the checkbox.
31170      * On is always based on a string comparison between inputValue and the param.
31171      * @param {Boolean/String} value - the value to set 
31172      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31173      */
31174     setValue : function(v,suppressEvent){
31175         if (!this.el.dom) {
31176             return;
31177         }
31178         var old = this.el.dom.value ;
31179         this.el.dom.value = v;
31180         if (suppressEvent) {
31181             return ;
31182         }
31183          
31184         // update display..
31185         this.viewEl.select('img',true).each(function(e,i,n)  {
31186             
31187             var on = e.is(".x-menu-item-checked");
31188             var newv = v.indexOf(String(n)) > -1;
31189             if (on != newv) {
31190                 e.toggleClass('x-menu-item-checked');
31191             }
31192             
31193         });
31194         
31195         
31196         this.fireEvent('change', this, v, old);
31197         
31198         
31199     },
31200    
31201     // handle setting of hidden value by some other method!!?!?
31202     setFromHidden: function()
31203     {
31204         if(!this.el){
31205             return;
31206         }
31207         //console.log("SET FROM HIDDEN");
31208         //alert('setFrom hidden');
31209         this.setValue(this.el.dom.value);
31210     },
31211     
31212     onDestroy : function()
31213     {
31214         if(this.viewEl){
31215             Roo.get(this.viewEl).remove();
31216         }
31217          
31218         Roo.form.DayPicker.superclass.onDestroy.call(this);
31219     }
31220
31221 });/*
31222  * RooJS Library 1.1.1
31223  * Copyright(c) 2008-2011  Alan Knowles
31224  *
31225  * License - LGPL
31226  */
31227  
31228
31229 /**
31230  * @class Roo.form.ComboCheck
31231  * @extends Roo.form.ComboBox
31232  * A combobox for multiple select items.
31233  *
31234  * FIXME - could do with a reset button..
31235  * 
31236  * @constructor
31237  * Create a new ComboCheck
31238  * @param {Object} config Configuration options
31239  */
31240 Roo.form.ComboCheck = function(config){
31241     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31242     // should verify some data...
31243     // like
31244     // hiddenName = required..
31245     // displayField = required
31246     // valudField == required
31247     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31248     var _t = this;
31249     Roo.each(req, function(e) {
31250         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31251             throw "Roo.form.ComboCheck : missing value for: " + e;
31252         }
31253     });
31254     
31255     
31256 };
31257
31258 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31259      
31260      
31261     editable : false,
31262      
31263     selectedClass: 'x-menu-item-checked', 
31264     
31265     // private
31266     onRender : function(ct, position){
31267         var _t = this;
31268         
31269         
31270         
31271         if(!this.tpl){
31272             var cls = 'x-combo-list';
31273
31274             
31275             this.tpl =  new Roo.Template({
31276                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31277                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31278                    '<span>{' + this.displayField + '}</span>' +
31279                     '</div>' 
31280                 
31281             });
31282         }
31283  
31284         
31285         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31286         this.view.singleSelect = false;
31287         this.view.multiSelect = true;
31288         this.view.toggleSelect = true;
31289         this.pageTb.add(new Roo.Toolbar.Fill(), {
31290             
31291             text: 'Done',
31292             handler: function()
31293             {
31294                 _t.collapse();
31295             }
31296         });
31297     },
31298     
31299     onViewOver : function(e, t){
31300         // do nothing...
31301         return;
31302         
31303     },
31304     
31305     onViewClick : function(doFocus,index){
31306         return;
31307         
31308     },
31309     select: function () {
31310         //Roo.log("SELECT CALLED");
31311     },
31312      
31313     selectByValue : function(xv, scrollIntoView){
31314         var ar = this.getValueArray();
31315         var sels = [];
31316         
31317         Roo.each(ar, function(v) {
31318             if(v === undefined || v === null){
31319                 return;
31320             }
31321             var r = this.findRecord(this.valueField, v);
31322             if(r){
31323                 sels.push(this.store.indexOf(r))
31324                 
31325             }
31326         },this);
31327         this.view.select(sels);
31328         return false;
31329     },
31330     
31331     
31332     
31333     onSelect : function(record, index){
31334        // Roo.log("onselect Called");
31335        // this is only called by the clear button now..
31336         this.view.clearSelections();
31337         this.setValue('[]');
31338         if (this.value != this.valueBefore) {
31339             this.fireEvent('change', this, this.value, this.valueBefore);
31340             this.valueBefore = this.value;
31341         }
31342     },
31343     getValueArray : function()
31344     {
31345         var ar = [] ;
31346         
31347         try {
31348             //Roo.log(this.value);
31349             if (typeof(this.value) == 'undefined') {
31350                 return [];
31351             }
31352             var ar = Roo.decode(this.value);
31353             return  ar instanceof Array ? ar : []; //?? valid?
31354             
31355         } catch(e) {
31356             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31357             return [];
31358         }
31359          
31360     },
31361     expand : function ()
31362     {
31363         
31364         Roo.form.ComboCheck.superclass.expand.call(this);
31365         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31366         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31367         
31368
31369     },
31370     
31371     collapse : function(){
31372         Roo.form.ComboCheck.superclass.collapse.call(this);
31373         var sl = this.view.getSelectedIndexes();
31374         var st = this.store;
31375         var nv = [];
31376         var tv = [];
31377         var r;
31378         Roo.each(sl, function(i) {
31379             r = st.getAt(i);
31380             nv.push(r.get(this.valueField));
31381         },this);
31382         this.setValue(Roo.encode(nv));
31383         if (this.value != this.valueBefore) {
31384
31385             this.fireEvent('change', this, this.value, this.valueBefore);
31386             this.valueBefore = this.value;
31387         }
31388         
31389     },
31390     
31391     setValue : function(v){
31392         // Roo.log(v);
31393         this.value = v;
31394         
31395         var vals = this.getValueArray();
31396         var tv = [];
31397         Roo.each(vals, function(k) {
31398             var r = this.findRecord(this.valueField, k);
31399             if(r){
31400                 tv.push(r.data[this.displayField]);
31401             }else if(this.valueNotFoundText !== undefined){
31402                 tv.push( this.valueNotFoundText );
31403             }
31404         },this);
31405        // Roo.log(tv);
31406         
31407         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31408         this.hiddenField.value = v;
31409         this.value = v;
31410     }
31411     
31412 });/*
31413  * Based on:
31414  * Ext JS Library 1.1.1
31415  * Copyright(c) 2006-2007, Ext JS, LLC.
31416  *
31417  * Originally Released Under LGPL - original licence link has changed is not relivant.
31418  *
31419  * Fork - LGPL
31420  * <script type="text/javascript">
31421  */
31422  
31423 /**
31424  * @class Roo.form.Signature
31425  * @extends Roo.form.Field
31426  * Signature field.  
31427  * @constructor
31428  * 
31429  * @param {Object} config Configuration options
31430  */
31431
31432 Roo.form.Signature = function(config){
31433     Roo.form.Signature.superclass.constructor.call(this, config);
31434     
31435     this.addEvents({// not in used??
31436          /**
31437          * @event confirm
31438          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31439              * @param {Roo.form.Signature} combo This combo box
31440              */
31441         'confirm' : true,
31442         /**
31443          * @event reset
31444          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31445              * @param {Roo.form.ComboBox} combo This combo box
31446              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31447              */
31448         'reset' : true
31449     });
31450 };
31451
31452 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31453     /**
31454      * @cfg {Object} labels Label to use when rendering a form.
31455      * defaults to 
31456      * labels : { 
31457      *      clear : "Clear",
31458      *      confirm : "Confirm"
31459      *  }
31460      */
31461     labels : { 
31462         clear : "Clear",
31463         confirm : "Confirm"
31464     },
31465     /**
31466      * @cfg {Number} width The signature panel width (defaults to 300)
31467      */
31468     width: 300,
31469     /**
31470      * @cfg {Number} height The signature panel height (defaults to 100)
31471      */
31472     height : 100,
31473     /**
31474      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31475      */
31476     allowBlank : false,
31477     
31478     //private
31479     // {Object} signPanel The signature SVG panel element (defaults to {})
31480     signPanel : {},
31481     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31482     isMouseDown : false,
31483     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31484     isConfirmed : false,
31485     // {String} signatureTmp SVG mapping string (defaults to empty string)
31486     signatureTmp : '',
31487     
31488     
31489     defaultAutoCreate : { // modified by initCompnoent..
31490         tag: "input",
31491         type:"hidden"
31492     },
31493
31494     // private
31495     onRender : function(ct, position){
31496         
31497         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31498         
31499         this.wrap = this.el.wrap({
31500             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31501         });
31502         
31503         this.createToolbar(this);
31504         this.signPanel = this.wrap.createChild({
31505                 tag: 'div',
31506                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31507             }, this.el
31508         );
31509             
31510         this.svgID = Roo.id();
31511         this.svgEl = this.signPanel.createChild({
31512               xmlns : 'http://www.w3.org/2000/svg',
31513               tag : 'svg',
31514               id : this.svgID + "-svg",
31515               width: this.width,
31516               height: this.height,
31517               viewBox: '0 0 '+this.width+' '+this.height,
31518               cn : [
31519                 {
31520                     tag: "rect",
31521                     id: this.svgID + "-svg-r",
31522                     width: this.width,
31523                     height: this.height,
31524                     fill: "#ffa"
31525                 },
31526                 {
31527                     tag: "line",
31528                     id: this.svgID + "-svg-l",
31529                     x1: "0", // start
31530                     y1: (this.height*0.8), // start set the line in 80% of height
31531                     x2: this.width, // end
31532                     y2: (this.height*0.8), // end set the line in 80% of height
31533                     'stroke': "#666",
31534                     'stroke-width': "1",
31535                     'stroke-dasharray': "3",
31536                     'shape-rendering': "crispEdges",
31537                     'pointer-events': "none"
31538                 },
31539                 {
31540                     tag: "path",
31541                     id: this.svgID + "-svg-p",
31542                     'stroke': "navy",
31543                     'stroke-width': "3",
31544                     'fill': "none",
31545                     'pointer-events': 'none'
31546                 }
31547               ]
31548         });
31549         this.createSVG();
31550         this.svgBox = this.svgEl.dom.getScreenCTM();
31551     },
31552     createSVG : function(){ 
31553         var svg = this.signPanel;
31554         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31555         var t = this;
31556
31557         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31558         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31559         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31560         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31561         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31562         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31563         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31564         
31565     },
31566     isTouchEvent : function(e){
31567         return e.type.match(/^touch/);
31568     },
31569     getCoords : function (e) {
31570         var pt    = this.svgEl.dom.createSVGPoint();
31571         pt.x = e.clientX; 
31572         pt.y = e.clientY;
31573         if (this.isTouchEvent(e)) {
31574             pt.x =  e.targetTouches[0].clientX;
31575             pt.y = e.targetTouches[0].clientY;
31576         }
31577         var a = this.svgEl.dom.getScreenCTM();
31578         var b = a.inverse();
31579         var mx = pt.matrixTransform(b);
31580         return mx.x + ',' + mx.y;
31581     },
31582     //mouse event headler 
31583     down : function (e) {
31584         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31585         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31586         
31587         this.isMouseDown = true;
31588         
31589         e.preventDefault();
31590     },
31591     move : function (e) {
31592         if (this.isMouseDown) {
31593             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31594             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31595         }
31596         
31597         e.preventDefault();
31598     },
31599     up : function (e) {
31600         this.isMouseDown = false;
31601         var sp = this.signatureTmp.split(' ');
31602         
31603         if(sp.length > 1){
31604             if(!sp[sp.length-2].match(/^L/)){
31605                 sp.pop();
31606                 sp.pop();
31607                 sp.push("");
31608                 this.signatureTmp = sp.join(" ");
31609             }
31610         }
31611         if(this.getValue() != this.signatureTmp){
31612             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31613             this.isConfirmed = false;
31614         }
31615         e.preventDefault();
31616     },
31617     
31618     /**
31619      * Protected method that will not generally be called directly. It
31620      * is called when the editor creates its toolbar. Override this method if you need to
31621      * add custom toolbar buttons.
31622      * @param {HtmlEditor} editor
31623      */
31624     createToolbar : function(editor){
31625          function btn(id, toggle, handler){
31626             var xid = fid + '-'+ id ;
31627             return {
31628                 id : xid,
31629                 cmd : id,
31630                 cls : 'x-btn-icon x-edit-'+id,
31631                 enableToggle:toggle !== false,
31632                 scope: editor, // was editor...
31633                 handler:handler||editor.relayBtnCmd,
31634                 clickEvent:'mousedown',
31635                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31636                 tabIndex:-1
31637             };
31638         }
31639         
31640         
31641         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31642         this.tb = tb;
31643         this.tb.add(
31644            {
31645                 cls : ' x-signature-btn x-signature-'+id,
31646                 scope: editor, // was editor...
31647                 handler: this.reset,
31648                 clickEvent:'mousedown',
31649                 text: this.labels.clear
31650             },
31651             {
31652                  xtype : 'Fill',
31653                  xns: Roo.Toolbar
31654             }, 
31655             {
31656                 cls : '  x-signature-btn x-signature-'+id,
31657                 scope: editor, // was editor...
31658                 handler: this.confirmHandler,
31659                 clickEvent:'mousedown',
31660                 text: this.labels.confirm
31661             }
31662         );
31663     
31664     },
31665     //public
31666     /**
31667      * when user is clicked confirm then show this image.....
31668      * 
31669      * @return {String} Image Data URI
31670      */
31671     getImageDataURI : function(){
31672         var svg = this.svgEl.dom.parentNode.innerHTML;
31673         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31674         return src; 
31675     },
31676     /**
31677      * 
31678      * @return {Boolean} this.isConfirmed
31679      */
31680     getConfirmed : function(){
31681         return this.isConfirmed;
31682     },
31683     /**
31684      * 
31685      * @return {Number} this.width
31686      */
31687     getWidth : function(){
31688         return this.width;
31689     },
31690     /**
31691      * 
31692      * @return {Number} this.height
31693      */
31694     getHeight : function(){
31695         return this.height;
31696     },
31697     // private
31698     getSignature : function(){
31699         return this.signatureTmp;
31700     },
31701     // private
31702     reset : function(){
31703         this.signatureTmp = '';
31704         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31705         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31706         this.isConfirmed = false;
31707         Roo.form.Signature.superclass.reset.call(this);
31708     },
31709     setSignature : function(s){
31710         this.signatureTmp = s;
31711         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31712         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31713         this.setValue(s);
31714         this.isConfirmed = false;
31715         Roo.form.Signature.superclass.reset.call(this);
31716     }, 
31717     test : function(){
31718 //        Roo.log(this.signPanel.dom.contentWindow.up())
31719     },
31720     //private
31721     setConfirmed : function(){
31722         
31723         
31724         
31725 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31726     },
31727     // private
31728     confirmHandler : function(){
31729         if(!this.getSignature()){
31730             return;
31731         }
31732         
31733         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31734         this.setValue(this.getSignature());
31735         this.isConfirmed = true;
31736         
31737         this.fireEvent('confirm', this);
31738     },
31739     // private
31740     // Subclasses should provide the validation implementation by overriding this
31741     validateValue : function(value){
31742         if(this.allowBlank){
31743             return true;
31744         }
31745         
31746         if(this.isConfirmed){
31747             return true;
31748         }
31749         return false;
31750     }
31751 });/*
31752  * Based on:
31753  * Ext JS Library 1.1.1
31754  * Copyright(c) 2006-2007, Ext JS, LLC.
31755  *
31756  * Originally Released Under LGPL - original licence link has changed is not relivant.
31757  *
31758  * Fork - LGPL
31759  * <script type="text/javascript">
31760  */
31761  
31762
31763 /**
31764  * @class Roo.form.ComboBox
31765  * @extends Roo.form.TriggerField
31766  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31767  * @constructor
31768  * Create a new ComboBox.
31769  * @param {Object} config Configuration options
31770  */
31771 Roo.form.Select = function(config){
31772     Roo.form.Select.superclass.constructor.call(this, config);
31773      
31774 };
31775
31776 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31777     /**
31778      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31779      */
31780     /**
31781      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31782      * rendering into an Roo.Editor, defaults to false)
31783      */
31784     /**
31785      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31786      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31787      */
31788     /**
31789      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31790      */
31791     /**
31792      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31793      * the dropdown list (defaults to undefined, with no header element)
31794      */
31795
31796      /**
31797      * @cfg {String/Roo.Template} tpl The template to use to render the output
31798      */
31799      
31800     // private
31801     defaultAutoCreate : {tag: "select"  },
31802     /**
31803      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31804      */
31805     listWidth: undefined,
31806     /**
31807      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31808      * mode = 'remote' or 'text' if mode = 'local')
31809      */
31810     displayField: undefined,
31811     /**
31812      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31813      * mode = 'remote' or 'value' if mode = 'local'). 
31814      * Note: use of a valueField requires the user make a selection
31815      * in order for a value to be mapped.
31816      */
31817     valueField: undefined,
31818     
31819     
31820     /**
31821      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31822      * field's data value (defaults to the underlying DOM element's name)
31823      */
31824     hiddenName: undefined,
31825     /**
31826      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31827      */
31828     listClass: '',
31829     /**
31830      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31831      */
31832     selectedClass: 'x-combo-selected',
31833     /**
31834      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31835      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31836      * which displays a downward arrow icon).
31837      */
31838     triggerClass : 'x-form-arrow-trigger',
31839     /**
31840      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31841      */
31842     shadow:'sides',
31843     /**
31844      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31845      * anchor positions (defaults to 'tl-bl')
31846      */
31847     listAlign: 'tl-bl?',
31848     /**
31849      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31850      */
31851     maxHeight: 300,
31852     /**
31853      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31854      * query specified by the allQuery config option (defaults to 'query')
31855      */
31856     triggerAction: 'query',
31857     /**
31858      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31859      * (defaults to 4, does not apply if editable = false)
31860      */
31861     minChars : 4,
31862     /**
31863      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31864      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31865      */
31866     typeAhead: false,
31867     /**
31868      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31869      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31870      */
31871     queryDelay: 500,
31872     /**
31873      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31874      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31875      */
31876     pageSize: 0,
31877     /**
31878      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31879      * when editable = true (defaults to false)
31880      */
31881     selectOnFocus:false,
31882     /**
31883      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31884      */
31885     queryParam: 'query',
31886     /**
31887      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31888      * when mode = 'remote' (defaults to 'Loading...')
31889      */
31890     loadingText: 'Loading...',
31891     /**
31892      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31893      */
31894     resizable: false,
31895     /**
31896      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31897      */
31898     handleHeight : 8,
31899     /**
31900      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31901      * traditional select (defaults to true)
31902      */
31903     editable: true,
31904     /**
31905      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31906      */
31907     allQuery: '',
31908     /**
31909      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31910      */
31911     mode: 'remote',
31912     /**
31913      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31914      * listWidth has a higher value)
31915      */
31916     minListWidth : 70,
31917     /**
31918      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31919      * allow the user to set arbitrary text into the field (defaults to false)
31920      */
31921     forceSelection:false,
31922     /**
31923      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31924      * if typeAhead = true (defaults to 250)
31925      */
31926     typeAheadDelay : 250,
31927     /**
31928      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31929      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31930      */
31931     valueNotFoundText : undefined,
31932     
31933     /**
31934      * @cfg {String} defaultValue The value displayed after loading the store.
31935      */
31936     defaultValue: '',
31937     
31938     /**
31939      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31940      */
31941     blockFocus : false,
31942     
31943     /**
31944      * @cfg {Boolean} disableClear Disable showing of clear button.
31945      */
31946     disableClear : false,
31947     /**
31948      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31949      */
31950     alwaysQuery : false,
31951     
31952     //private
31953     addicon : false,
31954     editicon: false,
31955     
31956     // element that contains real text value.. (when hidden is used..)
31957      
31958     // private
31959     onRender : function(ct, position){
31960         Roo.form.Field.prototype.onRender.call(this, ct, position);
31961         
31962         if(this.store){
31963             this.store.on('beforeload', this.onBeforeLoad, this);
31964             this.store.on('load', this.onLoad, this);
31965             this.store.on('loadexception', this.onLoadException, this);
31966             this.store.load({});
31967         }
31968         
31969         
31970         
31971     },
31972
31973     // private
31974     initEvents : function(){
31975         //Roo.form.ComboBox.superclass.initEvents.call(this);
31976  
31977     },
31978
31979     onDestroy : function(){
31980        
31981         if(this.store){
31982             this.store.un('beforeload', this.onBeforeLoad, this);
31983             this.store.un('load', this.onLoad, this);
31984             this.store.un('loadexception', this.onLoadException, this);
31985         }
31986         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31987     },
31988
31989     // private
31990     fireKey : function(e){
31991         if(e.isNavKeyPress() && !this.list.isVisible()){
31992             this.fireEvent("specialkey", this, e);
31993         }
31994     },
31995
31996     // private
31997     onResize: function(w, h){
31998         
31999         return; 
32000     
32001         
32002     },
32003
32004     /**
32005      * Allow or prevent the user from directly editing the field text.  If false is passed,
32006      * the user will only be able to select from the items defined in the dropdown list.  This method
32007      * is the runtime equivalent of setting the 'editable' config option at config time.
32008      * @param {Boolean} value True to allow the user to directly edit the field text
32009      */
32010     setEditable : function(value){
32011          
32012     },
32013
32014     // private
32015     onBeforeLoad : function(){
32016         
32017         Roo.log("Select before load");
32018         return;
32019     
32020         this.innerList.update(this.loadingText ?
32021                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32022         //this.restrictHeight();
32023         this.selectedIndex = -1;
32024     },
32025
32026     // private
32027     onLoad : function(){
32028
32029     
32030         var dom = this.el.dom;
32031         dom.innerHTML = '';
32032          var od = dom.ownerDocument;
32033          
32034         if (this.emptyText) {
32035             var op = od.createElement('option');
32036             op.setAttribute('value', '');
32037             op.innerHTML = String.format('{0}', this.emptyText);
32038             dom.appendChild(op);
32039         }
32040         if(this.store.getCount() > 0){
32041            
32042             var vf = this.valueField;
32043             var df = this.displayField;
32044             this.store.data.each(function(r) {
32045                 // which colmsn to use... testing - cdoe / title..
32046                 var op = od.createElement('option');
32047                 op.setAttribute('value', r.data[vf]);
32048                 op.innerHTML = String.format('{0}', r.data[df]);
32049                 dom.appendChild(op);
32050             });
32051             if (typeof(this.defaultValue != 'undefined')) {
32052                 this.setValue(this.defaultValue);
32053             }
32054             
32055              
32056         }else{
32057             //this.onEmptyResults();
32058         }
32059         //this.el.focus();
32060     },
32061     // private
32062     onLoadException : function()
32063     {
32064         dom.innerHTML = '';
32065             
32066         Roo.log("Select on load exception");
32067         return;
32068     
32069         this.collapse();
32070         Roo.log(this.store.reader.jsonData);
32071         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32072             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32073         }
32074         
32075         
32076     },
32077     // private
32078     onTypeAhead : function(){
32079          
32080     },
32081
32082     // private
32083     onSelect : function(record, index){
32084         Roo.log('on select?');
32085         return;
32086         if(this.fireEvent('beforeselect', this, record, index) !== false){
32087             this.setFromData(index > -1 ? record.data : false);
32088             this.collapse();
32089             this.fireEvent('select', this, record, index);
32090         }
32091     },
32092
32093     /**
32094      * Returns the currently selected field value or empty string if no value is set.
32095      * @return {String} value The selected value
32096      */
32097     getValue : function(){
32098         var dom = this.el.dom;
32099         this.value = dom.options[dom.selectedIndex].value;
32100         return this.value;
32101         
32102     },
32103
32104     /**
32105      * Clears any text/value currently set in the field
32106      */
32107     clearValue : function(){
32108         this.value = '';
32109         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32110         
32111     },
32112
32113     /**
32114      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32115      * will be displayed in the field.  If the value does not match the data value of an existing item,
32116      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32117      * Otherwise the field will be blank (although the value will still be set).
32118      * @param {String} value The value to match
32119      */
32120     setValue : function(v){
32121         var d = this.el.dom;
32122         for (var i =0; i < d.options.length;i++) {
32123             if (v == d.options[i].value) {
32124                 d.selectedIndex = i;
32125                 this.value = v;
32126                 return;
32127             }
32128         }
32129         this.clearValue();
32130     },
32131     /**
32132      * @property {Object} the last set data for the element
32133      */
32134     
32135     lastData : false,
32136     /**
32137      * Sets the value of the field based on a object which is related to the record format for the store.
32138      * @param {Object} value the value to set as. or false on reset?
32139      */
32140     setFromData : function(o){
32141         Roo.log('setfrom data?');
32142          
32143         
32144         
32145     },
32146     // private
32147     reset : function(){
32148         this.clearValue();
32149     },
32150     // private
32151     findRecord : function(prop, value){
32152         
32153         return false;
32154     
32155         var record;
32156         if(this.store.getCount() > 0){
32157             this.store.each(function(r){
32158                 if(r.data[prop] == value){
32159                     record = r;
32160                     return false;
32161                 }
32162                 return true;
32163             });
32164         }
32165         return record;
32166     },
32167     
32168     getName: function()
32169     {
32170         // returns hidden if it's set..
32171         if (!this.rendered) {return ''};
32172         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32173         
32174     },
32175      
32176
32177     
32178
32179     // private
32180     onEmptyResults : function(){
32181         Roo.log('empty results');
32182         //this.collapse();
32183     },
32184
32185     /**
32186      * Returns true if the dropdown list is expanded, else false.
32187      */
32188     isExpanded : function(){
32189         return false;
32190     },
32191
32192     /**
32193      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32194      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32195      * @param {String} value The data value of the item to select
32196      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32197      * selected item if it is not currently in view (defaults to true)
32198      * @return {Boolean} True if the value matched an item in the list, else false
32199      */
32200     selectByValue : function(v, scrollIntoView){
32201         Roo.log('select By Value');
32202         return false;
32203     
32204         if(v !== undefined && v !== null){
32205             var r = this.findRecord(this.valueField || this.displayField, v);
32206             if(r){
32207                 this.select(this.store.indexOf(r), scrollIntoView);
32208                 return true;
32209             }
32210         }
32211         return false;
32212     },
32213
32214     /**
32215      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32216      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32217      * @param {Number} index The zero-based index of the list item to select
32218      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32219      * selected item if it is not currently in view (defaults to true)
32220      */
32221     select : function(index, scrollIntoView){
32222         Roo.log('select ');
32223         return  ;
32224         
32225         this.selectedIndex = index;
32226         this.view.select(index);
32227         if(scrollIntoView !== false){
32228             var el = this.view.getNode(index);
32229             if(el){
32230                 this.innerList.scrollChildIntoView(el, false);
32231             }
32232         }
32233     },
32234
32235       
32236
32237     // private
32238     validateBlur : function(){
32239         
32240         return;
32241         
32242     },
32243
32244     // private
32245     initQuery : function(){
32246         this.doQuery(this.getRawValue());
32247     },
32248
32249     // private
32250     doForce : function(){
32251         if(this.el.dom.value.length > 0){
32252             this.el.dom.value =
32253                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32254              
32255         }
32256     },
32257
32258     /**
32259      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32260      * query allowing the query action to be canceled if needed.
32261      * @param {String} query The SQL query to execute
32262      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32263      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32264      * saved in the current store (defaults to false)
32265      */
32266     doQuery : function(q, forceAll){
32267         
32268         Roo.log('doQuery?');
32269         if(q === undefined || q === null){
32270             q = '';
32271         }
32272         var qe = {
32273             query: q,
32274             forceAll: forceAll,
32275             combo: this,
32276             cancel:false
32277         };
32278         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32279             return false;
32280         }
32281         q = qe.query;
32282         forceAll = qe.forceAll;
32283         if(forceAll === true || (q.length >= this.minChars)){
32284             if(this.lastQuery != q || this.alwaysQuery){
32285                 this.lastQuery = q;
32286                 if(this.mode == 'local'){
32287                     this.selectedIndex = -1;
32288                     if(forceAll){
32289                         this.store.clearFilter();
32290                     }else{
32291                         this.store.filter(this.displayField, q);
32292                     }
32293                     this.onLoad();
32294                 }else{
32295                     this.store.baseParams[this.queryParam] = q;
32296                     this.store.load({
32297                         params: this.getParams(q)
32298                     });
32299                     this.expand();
32300                 }
32301             }else{
32302                 this.selectedIndex = -1;
32303                 this.onLoad();   
32304             }
32305         }
32306     },
32307
32308     // private
32309     getParams : function(q){
32310         var p = {};
32311         //p[this.queryParam] = q;
32312         if(this.pageSize){
32313             p.start = 0;
32314             p.limit = this.pageSize;
32315         }
32316         return p;
32317     },
32318
32319     /**
32320      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32321      */
32322     collapse : function(){
32323         
32324     },
32325
32326     // private
32327     collapseIf : function(e){
32328         
32329     },
32330
32331     /**
32332      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32333      */
32334     expand : function(){
32335         
32336     } ,
32337
32338     // private
32339      
32340
32341     /** 
32342     * @cfg {Boolean} grow 
32343     * @hide 
32344     */
32345     /** 
32346     * @cfg {Number} growMin 
32347     * @hide 
32348     */
32349     /** 
32350     * @cfg {Number} growMax 
32351     * @hide 
32352     */
32353     /**
32354      * @hide
32355      * @method autoSize
32356      */
32357     
32358     setWidth : function()
32359     {
32360         
32361     },
32362     getResizeEl : function(){
32363         return this.el;
32364     }
32365 });//<script type="text/javasscript">
32366  
32367
32368 /**
32369  * @class Roo.DDView
32370  * A DnD enabled version of Roo.View.
32371  * @param {Element/String} container The Element in which to create the View.
32372  * @param {String} tpl The template string used to create the markup for each element of the View
32373  * @param {Object} config The configuration properties. These include all the config options of
32374  * {@link Roo.View} plus some specific to this class.<br>
32375  * <p>
32376  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32377  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32378  * <p>
32379  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32380 .x-view-drag-insert-above {
32381         border-top:1px dotted #3366cc;
32382 }
32383 .x-view-drag-insert-below {
32384         border-bottom:1px dotted #3366cc;
32385 }
32386 </code></pre>
32387  * 
32388  */
32389  
32390 Roo.DDView = function(container, tpl, config) {
32391     Roo.DDView.superclass.constructor.apply(this, arguments);
32392     this.getEl().setStyle("outline", "0px none");
32393     this.getEl().unselectable();
32394     if (this.dragGroup) {
32395                 this.setDraggable(this.dragGroup.split(","));
32396     }
32397     if (this.dropGroup) {
32398                 this.setDroppable(this.dropGroup.split(","));
32399     }
32400     if (this.deletable) {
32401         this.setDeletable();
32402     }
32403     this.isDirtyFlag = false;
32404         this.addEvents({
32405                 "drop" : true
32406         });
32407 };
32408
32409 Roo.extend(Roo.DDView, Roo.View, {
32410 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32411 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32412 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32413 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32414
32415         isFormField: true,
32416
32417         reset: Roo.emptyFn,
32418         
32419         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32420
32421         validate: function() {
32422                 return true;
32423         },
32424         
32425         destroy: function() {
32426                 this.purgeListeners();
32427                 this.getEl.removeAllListeners();
32428                 this.getEl().remove();
32429                 if (this.dragZone) {
32430                         if (this.dragZone.destroy) {
32431                                 this.dragZone.destroy();
32432                         }
32433                 }
32434                 if (this.dropZone) {
32435                         if (this.dropZone.destroy) {
32436                                 this.dropZone.destroy();
32437                         }
32438                 }
32439         },
32440
32441 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32442         getName: function() {
32443                 return this.name;
32444         },
32445
32446 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32447         setValue: function(v) {
32448                 if (!this.store) {
32449                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32450                 }
32451                 var data = {};
32452                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32453                 this.store.proxy = new Roo.data.MemoryProxy(data);
32454                 this.store.load();
32455         },
32456
32457 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32458         getValue: function() {
32459                 var result = '(';
32460                 this.store.each(function(rec) {
32461                         result += rec.id + ',';
32462                 });
32463                 return result.substr(0, result.length - 1) + ')';
32464         },
32465         
32466         getIds: function() {
32467                 var i = 0, result = new Array(this.store.getCount());
32468                 this.store.each(function(rec) {
32469                         result[i++] = rec.id;
32470                 });
32471                 return result;
32472         },
32473         
32474         isDirty: function() {
32475                 return this.isDirtyFlag;
32476         },
32477
32478 /**
32479  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32480  *      whole Element becomes the target, and this causes the drop gesture to append.
32481  */
32482     getTargetFromEvent : function(e) {
32483                 var target = e.getTarget();
32484                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32485                 target = target.parentNode;
32486                 }
32487                 if (!target) {
32488                         target = this.el.dom.lastChild || this.el.dom;
32489                 }
32490                 return target;
32491     },
32492
32493 /**
32494  *      Create the drag data which consists of an object which has the property "ddel" as
32495  *      the drag proxy element. 
32496  */
32497     getDragData : function(e) {
32498         var target = this.findItemFromChild(e.getTarget());
32499                 if(target) {
32500                         this.handleSelection(e);
32501                         var selNodes = this.getSelectedNodes();
32502             var dragData = {
32503                 source: this,
32504                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32505                 nodes: selNodes,
32506                 records: []
32507                         };
32508                         var selectedIndices = this.getSelectedIndexes();
32509                         for (var i = 0; i < selectedIndices.length; i++) {
32510                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32511                         }
32512                         if (selNodes.length == 1) {
32513                                 dragData.ddel = target.cloneNode(true); // the div element
32514                         } else {
32515                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32516                                 div.className = 'multi-proxy';
32517                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32518                                         div.appendChild(selNodes[i].cloneNode(true));
32519                                 }
32520                                 dragData.ddel = div;
32521                         }
32522             //console.log(dragData)
32523             //console.log(dragData.ddel.innerHTML)
32524                         return dragData;
32525                 }
32526         //console.log('nodragData')
32527                 return false;
32528     },
32529     
32530 /**     Specify to which ddGroup items in this DDView may be dragged. */
32531     setDraggable: function(ddGroup) {
32532         if (ddGroup instanceof Array) {
32533                 Roo.each(ddGroup, this.setDraggable, this);
32534                 return;
32535         }
32536         if (this.dragZone) {
32537                 this.dragZone.addToGroup(ddGroup);
32538         } else {
32539                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32540                                 containerScroll: true,
32541                                 ddGroup: ddGroup 
32542
32543                         });
32544 //                      Draggability implies selection. DragZone's mousedown selects the element.
32545                         if (!this.multiSelect) { this.singleSelect = true; }
32546
32547 //                      Wire the DragZone's handlers up to methods in *this*
32548                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32549                 }
32550     },
32551
32552 /**     Specify from which ddGroup this DDView accepts drops. */
32553     setDroppable: function(ddGroup) {
32554         if (ddGroup instanceof Array) {
32555                 Roo.each(ddGroup, this.setDroppable, this);
32556                 return;
32557         }
32558         if (this.dropZone) {
32559                 this.dropZone.addToGroup(ddGroup);
32560         } else {
32561                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32562                                 containerScroll: true,
32563                                 ddGroup: ddGroup
32564                         });
32565
32566 //                      Wire the DropZone's handlers up to methods in *this*
32567                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32568                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32569                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32570                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32571                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32572                 }
32573     },
32574
32575 /**     Decide whether to drop above or below a View node. */
32576     getDropPoint : function(e, n, dd){
32577         if (n == this.el.dom) { return "above"; }
32578                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32579                 var c = t + (b - t) / 2;
32580                 var y = Roo.lib.Event.getPageY(e);
32581                 if(y <= c) {
32582                         return "above";
32583                 }else{
32584                         return "below";
32585                 }
32586     },
32587
32588     onNodeEnter : function(n, dd, e, data){
32589                 return false;
32590     },
32591     
32592     onNodeOver : function(n, dd, e, data){
32593                 var pt = this.getDropPoint(e, n, dd);
32594                 // set the insert point style on the target node
32595                 var dragElClass = this.dropNotAllowed;
32596                 if (pt) {
32597                         var targetElClass;
32598                         if (pt == "above"){
32599                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32600                                 targetElClass = "x-view-drag-insert-above";
32601                         } else {
32602                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32603                                 targetElClass = "x-view-drag-insert-below";
32604                         }
32605                         if (this.lastInsertClass != targetElClass){
32606                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32607                                 this.lastInsertClass = targetElClass;
32608                         }
32609                 }
32610                 return dragElClass;
32611         },
32612
32613     onNodeOut : function(n, dd, e, data){
32614                 this.removeDropIndicators(n);
32615     },
32616
32617     onNodeDrop : function(n, dd, e, data){
32618         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32619                 return false;
32620         }
32621         var pt = this.getDropPoint(e, n, dd);
32622                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32623                 if (pt == "below") { insertAt++; }
32624                 for (var i = 0; i < data.records.length; i++) {
32625                         var r = data.records[i];
32626                         var dup = this.store.getById(r.id);
32627                         if (dup && (dd != this.dragZone)) {
32628                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32629                         } else {
32630                                 if (data.copy) {
32631                                         this.store.insert(insertAt++, r.copy());
32632                                 } else {
32633                                         data.source.isDirtyFlag = true;
32634                                         r.store.remove(r);
32635                                         this.store.insert(insertAt++, r);
32636                                 }
32637                                 this.isDirtyFlag = true;
32638                         }
32639                 }
32640                 this.dragZone.cachedTarget = null;
32641                 return true;
32642     },
32643
32644     removeDropIndicators : function(n){
32645                 if(n){
32646                         Roo.fly(n).removeClass([
32647                                 "x-view-drag-insert-above",
32648                                 "x-view-drag-insert-below"]);
32649                         this.lastInsertClass = "_noclass";
32650                 }
32651     },
32652
32653 /**
32654  *      Utility method. Add a delete option to the DDView's context menu.
32655  *      @param {String} imageUrl The URL of the "delete" icon image.
32656  */
32657         setDeletable: function(imageUrl) {
32658                 if (!this.singleSelect && !this.multiSelect) {
32659                         this.singleSelect = true;
32660                 }
32661                 var c = this.getContextMenu();
32662                 this.contextMenu.on("itemclick", function(item) {
32663                         switch (item.id) {
32664                                 case "delete":
32665                                         this.remove(this.getSelectedIndexes());
32666                                         break;
32667                         }
32668                 }, this);
32669                 this.contextMenu.add({
32670                         icon: imageUrl,
32671                         id: "delete",
32672                         text: 'Delete'
32673                 });
32674         },
32675         
32676 /**     Return the context menu for this DDView. */
32677         getContextMenu: function() {
32678                 if (!this.contextMenu) {
32679 //                      Create the View's context menu
32680                         this.contextMenu = new Roo.menu.Menu({
32681                                 id: this.id + "-contextmenu"
32682                         });
32683                         this.el.on("contextmenu", this.showContextMenu, this);
32684                 }
32685                 return this.contextMenu;
32686         },
32687         
32688         disableContextMenu: function() {
32689                 if (this.contextMenu) {
32690                         this.el.un("contextmenu", this.showContextMenu, this);
32691                 }
32692         },
32693
32694         showContextMenu: function(e, item) {
32695         item = this.findItemFromChild(e.getTarget());
32696                 if (item) {
32697                         e.stopEvent();
32698                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32699                         this.contextMenu.showAt(e.getXY());
32700             }
32701     },
32702
32703 /**
32704  *      Remove {@link Roo.data.Record}s at the specified indices.
32705  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32706  */
32707     remove: function(selectedIndices) {
32708                 selectedIndices = [].concat(selectedIndices);
32709                 for (var i = 0; i < selectedIndices.length; i++) {
32710                         var rec = this.store.getAt(selectedIndices[i]);
32711                         this.store.remove(rec);
32712                 }
32713     },
32714
32715 /**
32716  *      Double click fires the event, but also, if this is draggable, and there is only one other
32717  *      related DropZone, it transfers the selected node.
32718  */
32719     onDblClick : function(e){
32720         var item = this.findItemFromChild(e.getTarget());
32721         if(item){
32722             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32723                 return false;
32724             }
32725             if (this.dragGroup) {
32726                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32727                     while (targets.indexOf(this.dropZone) > -1) {
32728                             targets.remove(this.dropZone);
32729                                 }
32730                     if (targets.length == 1) {
32731                                         this.dragZone.cachedTarget = null;
32732                         var el = Roo.get(targets[0].getEl());
32733                         var box = el.getBox(true);
32734                         targets[0].onNodeDrop(el.dom, {
32735                                 target: el.dom,
32736                                 xy: [box.x, box.y + box.height - 1]
32737                         }, null, this.getDragData(e));
32738                     }
32739                 }
32740         }
32741     },
32742     
32743     handleSelection: function(e) {
32744                 this.dragZone.cachedTarget = null;
32745         var item = this.findItemFromChild(e.getTarget());
32746         if (!item) {
32747                 this.clearSelections(true);
32748                 return;
32749         }
32750                 if (item && (this.multiSelect || this.singleSelect)){
32751                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32752                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32753                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32754                                 this.unselect(item);
32755                         } else {
32756                                 this.select(item, this.multiSelect && e.ctrlKey);
32757                                 this.lastSelection = item;
32758                         }
32759                 }
32760     },
32761
32762     onItemClick : function(item, index, e){
32763                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32764                         return false;
32765                 }
32766                 return true;
32767     },
32768
32769     unselect : function(nodeInfo, suppressEvent){
32770                 var node = this.getNode(nodeInfo);
32771                 if(node && this.isSelected(node)){
32772                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32773                                 Roo.fly(node).removeClass(this.selectedClass);
32774                                 this.selections.remove(node);
32775                                 if(!suppressEvent){
32776                                         this.fireEvent("selectionchange", this, this.selections);
32777                                 }
32778                         }
32779                 }
32780     }
32781 });
32782 /*
32783  * Based on:
32784  * Ext JS Library 1.1.1
32785  * Copyright(c) 2006-2007, Ext JS, LLC.
32786  *
32787  * Originally Released Under LGPL - original licence link has changed is not relivant.
32788  *
32789  * Fork - LGPL
32790  * <script type="text/javascript">
32791  */
32792  
32793 /**
32794  * @class Roo.LayoutManager
32795  * @extends Roo.util.Observable
32796  * Base class for layout managers.
32797  */
32798 Roo.LayoutManager = function(container, config){
32799     Roo.LayoutManager.superclass.constructor.call(this);
32800     this.el = Roo.get(container);
32801     // ie scrollbar fix
32802     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32803         document.body.scroll = "no";
32804     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32805         this.el.position('relative');
32806     }
32807     this.id = this.el.id;
32808     this.el.addClass("x-layout-container");
32809     /** false to disable window resize monitoring @type Boolean */
32810     this.monitorWindowResize = true;
32811     this.regions = {};
32812     this.addEvents({
32813         /**
32814          * @event layout
32815          * Fires when a layout is performed. 
32816          * @param {Roo.LayoutManager} this
32817          */
32818         "layout" : true,
32819         /**
32820          * @event regionresized
32821          * Fires when the user resizes a region. 
32822          * @param {Roo.LayoutRegion} region The resized region
32823          * @param {Number} newSize The new size (width for east/west, height for north/south)
32824          */
32825         "regionresized" : true,
32826         /**
32827          * @event regioncollapsed
32828          * Fires when a region is collapsed. 
32829          * @param {Roo.LayoutRegion} region The collapsed region
32830          */
32831         "regioncollapsed" : true,
32832         /**
32833          * @event regionexpanded
32834          * Fires when a region is expanded.  
32835          * @param {Roo.LayoutRegion} region The expanded region
32836          */
32837         "regionexpanded" : true
32838     });
32839     this.updating = false;
32840     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32841 };
32842
32843 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32844     /**
32845      * Returns true if this layout is currently being updated
32846      * @return {Boolean}
32847      */
32848     isUpdating : function(){
32849         return this.updating; 
32850     },
32851     
32852     /**
32853      * Suspend the LayoutManager from doing auto-layouts while
32854      * making multiple add or remove calls
32855      */
32856     beginUpdate : function(){
32857         this.updating = true;    
32858     },
32859     
32860     /**
32861      * Restore auto-layouts and optionally disable the manager from performing a layout
32862      * @param {Boolean} noLayout true to disable a layout update 
32863      */
32864     endUpdate : function(noLayout){
32865         this.updating = false;
32866         if(!noLayout){
32867             this.layout();
32868         }    
32869     },
32870     
32871     layout: function(){
32872         
32873     },
32874     
32875     onRegionResized : function(region, newSize){
32876         this.fireEvent("regionresized", region, newSize);
32877         this.layout();
32878     },
32879     
32880     onRegionCollapsed : function(region){
32881         this.fireEvent("regioncollapsed", region);
32882     },
32883     
32884     onRegionExpanded : function(region){
32885         this.fireEvent("regionexpanded", region);
32886     },
32887         
32888     /**
32889      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32890      * performs box-model adjustments.
32891      * @return {Object} The size as an object {width: (the width), height: (the height)}
32892      */
32893     getViewSize : function(){
32894         var size;
32895         if(this.el.dom != document.body){
32896             size = this.el.getSize();
32897         }else{
32898             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32899         }
32900         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32901         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32902         return size;
32903     },
32904     
32905     /**
32906      * Returns the Element this layout is bound to.
32907      * @return {Roo.Element}
32908      */
32909     getEl : function(){
32910         return this.el;
32911     },
32912     
32913     /**
32914      * Returns the specified region.
32915      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32916      * @return {Roo.LayoutRegion}
32917      */
32918     getRegion : function(target){
32919         return this.regions[target.toLowerCase()];
32920     },
32921     
32922     onWindowResize : function(){
32923         if(this.monitorWindowResize){
32924             this.layout();
32925         }
32926     }
32927 });/*
32928  * Based on:
32929  * Ext JS Library 1.1.1
32930  * Copyright(c) 2006-2007, Ext JS, LLC.
32931  *
32932  * Originally Released Under LGPL - original licence link has changed is not relivant.
32933  *
32934  * Fork - LGPL
32935  * <script type="text/javascript">
32936  */
32937 /**
32938  * @class Roo.BorderLayout
32939  * @extends Roo.LayoutManager
32940  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32941  * please see: <br><br>
32942  * <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>
32943  * <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>
32944  * Example:
32945  <pre><code>
32946  var layout = new Roo.BorderLayout(document.body, {
32947     north: {
32948         initialSize: 25,
32949         titlebar: false
32950     },
32951     west: {
32952         split:true,
32953         initialSize: 200,
32954         minSize: 175,
32955         maxSize: 400,
32956         titlebar: true,
32957         collapsible: true
32958     },
32959     east: {
32960         split:true,
32961         initialSize: 202,
32962         minSize: 175,
32963         maxSize: 400,
32964         titlebar: true,
32965         collapsible: true
32966     },
32967     south: {
32968         split:true,
32969         initialSize: 100,
32970         minSize: 100,
32971         maxSize: 200,
32972         titlebar: true,
32973         collapsible: true
32974     },
32975     center: {
32976         titlebar: true,
32977         autoScroll:true,
32978         resizeTabs: true,
32979         minTabWidth: 50,
32980         preferredTabWidth: 150
32981     }
32982 });
32983
32984 // shorthand
32985 var CP = Roo.ContentPanel;
32986
32987 layout.beginUpdate();
32988 layout.add("north", new CP("north", "North"));
32989 layout.add("south", new CP("south", {title: "South", closable: true}));
32990 layout.add("west", new CP("west", {title: "West"}));
32991 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32992 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32993 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32994 layout.getRegion("center").showPanel("center1");
32995 layout.endUpdate();
32996 </code></pre>
32997
32998 <b>The container the layout is rendered into can be either the body element or any other element.
32999 If it is not the body element, the container needs to either be an absolute positioned element,
33000 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33001 the container size if it is not the body element.</b>
33002
33003 * @constructor
33004 * Create a new BorderLayout
33005 * @param {String/HTMLElement/Element} container The container this layout is bound to
33006 * @param {Object} config Configuration options
33007  */
33008 Roo.BorderLayout = function(container, config){
33009     config = config || {};
33010     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33011     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33012     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33013         var target = this.factory.validRegions[i];
33014         if(config[target]){
33015             this.addRegion(target, config[target]);
33016         }
33017     }
33018 };
33019
33020 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33021     /**
33022      * Creates and adds a new region if it doesn't already exist.
33023      * @param {String} target The target region key (north, south, east, west or center).
33024      * @param {Object} config The regions config object
33025      * @return {BorderLayoutRegion} The new region
33026      */
33027     addRegion : function(target, config){
33028         if(!this.regions[target]){
33029             var r = this.factory.create(target, this, config);
33030             this.bindRegion(target, r);
33031         }
33032         return this.regions[target];
33033     },
33034
33035     // private (kinda)
33036     bindRegion : function(name, r){
33037         this.regions[name] = r;
33038         r.on("visibilitychange", this.layout, this);
33039         r.on("paneladded", this.layout, this);
33040         r.on("panelremoved", this.layout, this);
33041         r.on("invalidated", this.layout, this);
33042         r.on("resized", this.onRegionResized, this);
33043         r.on("collapsed", this.onRegionCollapsed, this);
33044         r.on("expanded", this.onRegionExpanded, this);
33045     },
33046
33047     /**
33048      * Performs a layout update.
33049      */
33050     layout : function(){
33051         if(this.updating) {
33052             return;
33053         }
33054         var size = this.getViewSize();
33055         var w = size.width;
33056         var h = size.height;
33057         var centerW = w;
33058         var centerH = h;
33059         var centerY = 0;
33060         var centerX = 0;
33061         //var x = 0, y = 0;
33062
33063         var rs = this.regions;
33064         var north = rs["north"];
33065         var south = rs["south"]; 
33066         var west = rs["west"];
33067         var east = rs["east"];
33068         var center = rs["center"];
33069         //if(this.hideOnLayout){ // not supported anymore
33070             //c.el.setStyle("display", "none");
33071         //}
33072         if(north && north.isVisible()){
33073             var b = north.getBox();
33074             var m = north.getMargins();
33075             b.width = w - (m.left+m.right);
33076             b.x = m.left;
33077             b.y = m.top;
33078             centerY = b.height + b.y + m.bottom;
33079             centerH -= centerY;
33080             north.updateBox(this.safeBox(b));
33081         }
33082         if(south && south.isVisible()){
33083             var b = south.getBox();
33084             var m = south.getMargins();
33085             b.width = w - (m.left+m.right);
33086             b.x = m.left;
33087             var totalHeight = (b.height + m.top + m.bottom);
33088             b.y = h - totalHeight + m.top;
33089             centerH -= totalHeight;
33090             south.updateBox(this.safeBox(b));
33091         }
33092         if(west && west.isVisible()){
33093             var b = west.getBox();
33094             var m = west.getMargins();
33095             b.height = centerH - (m.top+m.bottom);
33096             b.x = m.left;
33097             b.y = centerY + m.top;
33098             var totalWidth = (b.width + m.left + m.right);
33099             centerX += totalWidth;
33100             centerW -= totalWidth;
33101             west.updateBox(this.safeBox(b));
33102         }
33103         if(east && east.isVisible()){
33104             var b = east.getBox();
33105             var m = east.getMargins();
33106             b.height = centerH - (m.top+m.bottom);
33107             var totalWidth = (b.width + m.left + m.right);
33108             b.x = w - totalWidth + m.left;
33109             b.y = centerY + m.top;
33110             centerW -= totalWidth;
33111             east.updateBox(this.safeBox(b));
33112         }
33113         if(center){
33114             var m = center.getMargins();
33115             var centerBox = {
33116                 x: centerX + m.left,
33117                 y: centerY + m.top,
33118                 width: centerW - (m.left+m.right),
33119                 height: centerH - (m.top+m.bottom)
33120             };
33121             //if(this.hideOnLayout){
33122                 //center.el.setStyle("display", "block");
33123             //}
33124             center.updateBox(this.safeBox(centerBox));
33125         }
33126         this.el.repaint();
33127         this.fireEvent("layout", this);
33128     },
33129
33130     // private
33131     safeBox : function(box){
33132         box.width = Math.max(0, box.width);
33133         box.height = Math.max(0, box.height);
33134         return box;
33135     },
33136
33137     /**
33138      * Adds a ContentPanel (or subclass) to this layout.
33139      * @param {String} target The target region key (north, south, east, west or center).
33140      * @param {Roo.ContentPanel} panel The panel to add
33141      * @return {Roo.ContentPanel} The added panel
33142      */
33143     add : function(target, panel){
33144          
33145         target = target.toLowerCase();
33146         return this.regions[target].add(panel);
33147     },
33148
33149     /**
33150      * Remove a ContentPanel (or subclass) to this layout.
33151      * @param {String} target The target region key (north, south, east, west or center).
33152      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33153      * @return {Roo.ContentPanel} The removed panel
33154      */
33155     remove : function(target, panel){
33156         target = target.toLowerCase();
33157         return this.regions[target].remove(panel);
33158     },
33159
33160     /**
33161      * Searches all regions for a panel with the specified id
33162      * @param {String} panelId
33163      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33164      */
33165     findPanel : function(panelId){
33166         var rs = this.regions;
33167         for(var target in rs){
33168             if(typeof rs[target] != "function"){
33169                 var p = rs[target].getPanel(panelId);
33170                 if(p){
33171                     return p;
33172                 }
33173             }
33174         }
33175         return null;
33176     },
33177
33178     /**
33179      * Searches all regions for a panel with the specified id and activates (shows) it.
33180      * @param {String/ContentPanel} panelId The panels id or the panel itself
33181      * @return {Roo.ContentPanel} The shown panel or null
33182      */
33183     showPanel : function(panelId) {
33184       var rs = this.regions;
33185       for(var target in rs){
33186          var r = rs[target];
33187          if(typeof r != "function"){
33188             if(r.hasPanel(panelId)){
33189                return r.showPanel(panelId);
33190             }
33191          }
33192       }
33193       return null;
33194    },
33195
33196    /**
33197      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33198      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33199      */
33200     restoreState : function(provider){
33201         if(!provider){
33202             provider = Roo.state.Manager;
33203         }
33204         var sm = new Roo.LayoutStateManager();
33205         sm.init(this, provider);
33206     },
33207
33208     /**
33209      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33210      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33211      * a valid ContentPanel config object.  Example:
33212      * <pre><code>
33213 // Create the main layout
33214 var layout = new Roo.BorderLayout('main-ct', {
33215     west: {
33216         split:true,
33217         minSize: 175,
33218         titlebar: true
33219     },
33220     center: {
33221         title:'Components'
33222     }
33223 }, 'main-ct');
33224
33225 // Create and add multiple ContentPanels at once via configs
33226 layout.batchAdd({
33227    west: {
33228        id: 'source-files',
33229        autoCreate:true,
33230        title:'Ext Source Files',
33231        autoScroll:true,
33232        fitToFrame:true
33233    },
33234    center : {
33235        el: cview,
33236        autoScroll:true,
33237        fitToFrame:true,
33238        toolbar: tb,
33239        resizeEl:'cbody'
33240    }
33241 });
33242 </code></pre>
33243      * @param {Object} regions An object containing ContentPanel configs by region name
33244      */
33245     batchAdd : function(regions){
33246         this.beginUpdate();
33247         for(var rname in regions){
33248             var lr = this.regions[rname];
33249             if(lr){
33250                 this.addTypedPanels(lr, regions[rname]);
33251             }
33252         }
33253         this.endUpdate();
33254     },
33255
33256     // private
33257     addTypedPanels : function(lr, ps){
33258         if(typeof ps == 'string'){
33259             lr.add(new Roo.ContentPanel(ps));
33260         }
33261         else if(ps instanceof Array){
33262             for(var i =0, len = ps.length; i < len; i++){
33263                 this.addTypedPanels(lr, ps[i]);
33264             }
33265         }
33266         else if(!ps.events){ // raw config?
33267             var el = ps.el;
33268             delete ps.el; // prevent conflict
33269             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33270         }
33271         else {  // panel object assumed!
33272             lr.add(ps);
33273         }
33274     },
33275     /**
33276      * Adds a xtype elements to the layout.
33277      * <pre><code>
33278
33279 layout.addxtype({
33280        xtype : 'ContentPanel',
33281        region: 'west',
33282        items: [ .... ]
33283    }
33284 );
33285
33286 layout.addxtype({
33287         xtype : 'NestedLayoutPanel',
33288         region: 'west',
33289         layout: {
33290            center: { },
33291            west: { }   
33292         },
33293         items : [ ... list of content panels or nested layout panels.. ]
33294    }
33295 );
33296 </code></pre>
33297      * @param {Object} cfg Xtype definition of item to add.
33298      */
33299     addxtype : function(cfg)
33300     {
33301         // basically accepts a pannel...
33302         // can accept a layout region..!?!?
33303         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33304         
33305         if (!cfg.xtype.match(/Panel$/)) {
33306             return false;
33307         }
33308         var ret = false;
33309         
33310         if (typeof(cfg.region) == 'undefined') {
33311             Roo.log("Failed to add Panel, region was not set");
33312             Roo.log(cfg);
33313             return false;
33314         }
33315         var region = cfg.region;
33316         delete cfg.region;
33317         
33318           
33319         var xitems = [];
33320         if (cfg.items) {
33321             xitems = cfg.items;
33322             delete cfg.items;
33323         }
33324         var nb = false;
33325         
33326         switch(cfg.xtype) 
33327         {
33328             case 'ContentPanel':  // ContentPanel (el, cfg)
33329             case 'ScrollPanel':  // ContentPanel (el, cfg)
33330             case 'ViewPanel': 
33331                 if(cfg.autoCreate) {
33332                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33333                 } else {
33334                     var el = this.el.createChild();
33335                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33336                 }
33337                 
33338                 this.add(region, ret);
33339                 break;
33340             
33341             
33342             case 'TreePanel': // our new panel!
33343                 cfg.el = this.el.createChild();
33344                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33345                 this.add(region, ret);
33346                 break;
33347             
33348             case 'NestedLayoutPanel': 
33349                 // create a new Layout (which is  a Border Layout...
33350                 var el = this.el.createChild();
33351                 var clayout = cfg.layout;
33352                 delete cfg.layout;
33353                 clayout.items   = clayout.items  || [];
33354                 // replace this exitems with the clayout ones..
33355                 xitems = clayout.items;
33356                  
33357                 
33358                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33359                     cfg.background = false;
33360                 }
33361                 var layout = new Roo.BorderLayout(el, clayout);
33362                 
33363                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33364                 //console.log('adding nested layout panel '  + cfg.toSource());
33365                 this.add(region, ret);
33366                 nb = {}; /// find first...
33367                 break;
33368                 
33369             case 'GridPanel': 
33370             
33371                 // needs grid and region
33372                 
33373                 //var el = this.getRegion(region).el.createChild();
33374                 var el = this.el.createChild();
33375                 // create the grid first...
33376                 
33377                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33378                 delete cfg.grid;
33379                 if (region == 'center' && this.active ) {
33380                     cfg.background = false;
33381                 }
33382                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33383                 
33384                 this.add(region, ret);
33385                 if (cfg.background) {
33386                     ret.on('activate', function(gp) {
33387                         if (!gp.grid.rendered) {
33388                             gp.grid.render();
33389                         }
33390                     });
33391                 } else {
33392                     grid.render();
33393                 }
33394                 break;
33395            
33396            
33397            
33398                 
33399                 
33400                 
33401             default:
33402                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33403                     
33404                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33405                     this.add(region, ret);
33406                 } else {
33407                 
33408                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33409                     return null;
33410                 }
33411                 
33412              // GridPanel (grid, cfg)
33413             
33414         }
33415         this.beginUpdate();
33416         // add children..
33417         var region = '';
33418         var abn = {};
33419         Roo.each(xitems, function(i)  {
33420             region = nb && i.region ? i.region : false;
33421             
33422             var add = ret.addxtype(i);
33423            
33424             if (region) {
33425                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33426                 if (!i.background) {
33427                     abn[region] = nb[region] ;
33428                 }
33429             }
33430             
33431         });
33432         this.endUpdate();
33433
33434         // make the last non-background panel active..
33435         //if (nb) { Roo.log(abn); }
33436         if (nb) {
33437             
33438             for(var r in abn) {
33439                 region = this.getRegion(r);
33440                 if (region) {
33441                     // tried using nb[r], but it does not work..
33442                      
33443                     region.showPanel(abn[r]);
33444                    
33445                 }
33446             }
33447         }
33448         return ret;
33449         
33450     }
33451 });
33452
33453 /**
33454  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33455  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33456  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33457  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33458  * <pre><code>
33459 // shorthand
33460 var CP = Roo.ContentPanel;
33461
33462 var layout = Roo.BorderLayout.create({
33463     north: {
33464         initialSize: 25,
33465         titlebar: false,
33466         panels: [new CP("north", "North")]
33467     },
33468     west: {
33469         split:true,
33470         initialSize: 200,
33471         minSize: 175,
33472         maxSize: 400,
33473         titlebar: true,
33474         collapsible: true,
33475         panels: [new CP("west", {title: "West"})]
33476     },
33477     east: {
33478         split:true,
33479         initialSize: 202,
33480         minSize: 175,
33481         maxSize: 400,
33482         titlebar: true,
33483         collapsible: true,
33484         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33485     },
33486     south: {
33487         split:true,
33488         initialSize: 100,
33489         minSize: 100,
33490         maxSize: 200,
33491         titlebar: true,
33492         collapsible: true,
33493         panels: [new CP("south", {title: "South", closable: true})]
33494     },
33495     center: {
33496         titlebar: true,
33497         autoScroll:true,
33498         resizeTabs: true,
33499         minTabWidth: 50,
33500         preferredTabWidth: 150,
33501         panels: [
33502             new CP("center1", {title: "Close Me", closable: true}),
33503             new CP("center2", {title: "Center Panel", closable: false})
33504         ]
33505     }
33506 }, document.body);
33507
33508 layout.getRegion("center").showPanel("center1");
33509 </code></pre>
33510  * @param config
33511  * @param targetEl
33512  */
33513 Roo.BorderLayout.create = function(config, targetEl){
33514     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33515     layout.beginUpdate();
33516     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33517     for(var j = 0, jlen = regions.length; j < jlen; j++){
33518         var lr = regions[j];
33519         if(layout.regions[lr] && config[lr].panels){
33520             var r = layout.regions[lr];
33521             var ps = config[lr].panels;
33522             layout.addTypedPanels(r, ps);
33523         }
33524     }
33525     layout.endUpdate();
33526     return layout;
33527 };
33528
33529 // private
33530 Roo.BorderLayout.RegionFactory = {
33531     // private
33532     validRegions : ["north","south","east","west","center"],
33533
33534     // private
33535     create : function(target, mgr, config){
33536         target = target.toLowerCase();
33537         if(config.lightweight || config.basic){
33538             return new Roo.BasicLayoutRegion(mgr, config, target);
33539         }
33540         switch(target){
33541             case "north":
33542                 return new Roo.NorthLayoutRegion(mgr, config);
33543             case "south":
33544                 return new Roo.SouthLayoutRegion(mgr, config);
33545             case "east":
33546                 return new Roo.EastLayoutRegion(mgr, config);
33547             case "west":
33548                 return new Roo.WestLayoutRegion(mgr, config);
33549             case "center":
33550                 return new Roo.CenterLayoutRegion(mgr, config);
33551         }
33552         throw 'Layout region "'+target+'" not supported.';
33553     }
33554 };/*
33555  * Based on:
33556  * Ext JS Library 1.1.1
33557  * Copyright(c) 2006-2007, Ext JS, LLC.
33558  *
33559  * Originally Released Under LGPL - original licence link has changed is not relivant.
33560  *
33561  * Fork - LGPL
33562  * <script type="text/javascript">
33563  */
33564  
33565 /**
33566  * @class Roo.BasicLayoutRegion
33567  * @extends Roo.util.Observable
33568  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33569  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33570  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33571  */
33572 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33573     this.mgr = mgr;
33574     this.position  = pos;
33575     this.events = {
33576         /**
33577          * @scope Roo.BasicLayoutRegion
33578          */
33579         
33580         /**
33581          * @event beforeremove
33582          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33583          * @param {Roo.LayoutRegion} this
33584          * @param {Roo.ContentPanel} panel The panel
33585          * @param {Object} e The cancel event object
33586          */
33587         "beforeremove" : true,
33588         /**
33589          * @event invalidated
33590          * Fires when the layout for this region is changed.
33591          * @param {Roo.LayoutRegion} this
33592          */
33593         "invalidated" : true,
33594         /**
33595          * @event visibilitychange
33596          * Fires when this region is shown or hidden 
33597          * @param {Roo.LayoutRegion} this
33598          * @param {Boolean} visibility true or false
33599          */
33600         "visibilitychange" : true,
33601         /**
33602          * @event paneladded
33603          * Fires when a panel is added. 
33604          * @param {Roo.LayoutRegion} this
33605          * @param {Roo.ContentPanel} panel The panel
33606          */
33607         "paneladded" : true,
33608         /**
33609          * @event panelremoved
33610          * Fires when a panel is removed. 
33611          * @param {Roo.LayoutRegion} this
33612          * @param {Roo.ContentPanel} panel The panel
33613          */
33614         "panelremoved" : true,
33615         /**
33616          * @event collapsed
33617          * Fires when this region is collapsed.
33618          * @param {Roo.LayoutRegion} this
33619          */
33620         "collapsed" : true,
33621         /**
33622          * @event expanded
33623          * Fires when this region is expanded.
33624          * @param {Roo.LayoutRegion} this
33625          */
33626         "expanded" : true,
33627         /**
33628          * @event slideshow
33629          * Fires when this region is slid into view.
33630          * @param {Roo.LayoutRegion} this
33631          */
33632         "slideshow" : true,
33633         /**
33634          * @event slidehide
33635          * Fires when this region slides out of view. 
33636          * @param {Roo.LayoutRegion} this
33637          */
33638         "slidehide" : true,
33639         /**
33640          * @event panelactivated
33641          * Fires when a panel is activated. 
33642          * @param {Roo.LayoutRegion} this
33643          * @param {Roo.ContentPanel} panel The activated panel
33644          */
33645         "panelactivated" : true,
33646         /**
33647          * @event resized
33648          * Fires when the user resizes this region. 
33649          * @param {Roo.LayoutRegion} this
33650          * @param {Number} newSize The new size (width for east/west, height for north/south)
33651          */
33652         "resized" : true
33653     };
33654     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33655     this.panels = new Roo.util.MixedCollection();
33656     this.panels.getKey = this.getPanelId.createDelegate(this);
33657     this.box = null;
33658     this.activePanel = null;
33659     // ensure listeners are added...
33660     
33661     if (config.listeners || config.events) {
33662         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33663             listeners : config.listeners || {},
33664             events : config.events || {}
33665         });
33666     }
33667     
33668     if(skipConfig !== true){
33669         this.applyConfig(config);
33670     }
33671 };
33672
33673 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33674     getPanelId : function(p){
33675         return p.getId();
33676     },
33677     
33678     applyConfig : function(config){
33679         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33680         this.config = config;
33681         
33682     },
33683     
33684     /**
33685      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33686      * the width, for horizontal (north, south) the height.
33687      * @param {Number} newSize The new width or height
33688      */
33689     resizeTo : function(newSize){
33690         var el = this.el ? this.el :
33691                  (this.activePanel ? this.activePanel.getEl() : null);
33692         if(el){
33693             switch(this.position){
33694                 case "east":
33695                 case "west":
33696                     el.setWidth(newSize);
33697                     this.fireEvent("resized", this, newSize);
33698                 break;
33699                 case "north":
33700                 case "south":
33701                     el.setHeight(newSize);
33702                     this.fireEvent("resized", this, newSize);
33703                 break;                
33704             }
33705         }
33706     },
33707     
33708     getBox : function(){
33709         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33710     },
33711     
33712     getMargins : function(){
33713         return this.margins;
33714     },
33715     
33716     updateBox : function(box){
33717         this.box = box;
33718         var el = this.activePanel.getEl();
33719         el.dom.style.left = box.x + "px";
33720         el.dom.style.top = box.y + "px";
33721         this.activePanel.setSize(box.width, box.height);
33722     },
33723     
33724     /**
33725      * Returns the container element for this region.
33726      * @return {Roo.Element}
33727      */
33728     getEl : function(){
33729         return this.activePanel;
33730     },
33731     
33732     /**
33733      * Returns true if this region is currently visible.
33734      * @return {Boolean}
33735      */
33736     isVisible : function(){
33737         return this.activePanel ? true : false;
33738     },
33739     
33740     setActivePanel : function(panel){
33741         panel = this.getPanel(panel);
33742         if(this.activePanel && this.activePanel != panel){
33743             this.activePanel.setActiveState(false);
33744             this.activePanel.getEl().setLeftTop(-10000,-10000);
33745         }
33746         this.activePanel = panel;
33747         panel.setActiveState(true);
33748         if(this.box){
33749             panel.setSize(this.box.width, this.box.height);
33750         }
33751         this.fireEvent("panelactivated", this, panel);
33752         this.fireEvent("invalidated");
33753     },
33754     
33755     /**
33756      * Show the specified panel.
33757      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33758      * @return {Roo.ContentPanel} The shown panel or null
33759      */
33760     showPanel : function(panel){
33761         if(panel = this.getPanel(panel)){
33762             this.setActivePanel(panel);
33763         }
33764         return panel;
33765     },
33766     
33767     /**
33768      * Get the active panel for this region.
33769      * @return {Roo.ContentPanel} The active panel or null
33770      */
33771     getActivePanel : function(){
33772         return this.activePanel;
33773     },
33774     
33775     /**
33776      * Add the passed ContentPanel(s)
33777      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33778      * @return {Roo.ContentPanel} The panel added (if only one was added)
33779      */
33780     add : function(panel){
33781         if(arguments.length > 1){
33782             for(var i = 0, len = arguments.length; i < len; i++) {
33783                 this.add(arguments[i]);
33784             }
33785             return null;
33786         }
33787         if(this.hasPanel(panel)){
33788             this.showPanel(panel);
33789             return panel;
33790         }
33791         var el = panel.getEl();
33792         if(el.dom.parentNode != this.mgr.el.dom){
33793             this.mgr.el.dom.appendChild(el.dom);
33794         }
33795         if(panel.setRegion){
33796             panel.setRegion(this);
33797         }
33798         this.panels.add(panel);
33799         el.setStyle("position", "absolute");
33800         if(!panel.background){
33801             this.setActivePanel(panel);
33802             if(this.config.initialSize && this.panels.getCount()==1){
33803                 this.resizeTo(this.config.initialSize);
33804             }
33805         }
33806         this.fireEvent("paneladded", this, panel);
33807         return panel;
33808     },
33809     
33810     /**
33811      * Returns true if the panel is in this region.
33812      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33813      * @return {Boolean}
33814      */
33815     hasPanel : function(panel){
33816         if(typeof panel == "object"){ // must be panel obj
33817             panel = panel.getId();
33818         }
33819         return this.getPanel(panel) ? true : false;
33820     },
33821     
33822     /**
33823      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33824      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33825      * @param {Boolean} preservePanel Overrides the config preservePanel option
33826      * @return {Roo.ContentPanel} The panel that was removed
33827      */
33828     remove : function(panel, preservePanel){
33829         panel = this.getPanel(panel);
33830         if(!panel){
33831             return null;
33832         }
33833         var e = {};
33834         this.fireEvent("beforeremove", this, panel, e);
33835         if(e.cancel === true){
33836             return null;
33837         }
33838         var panelId = panel.getId();
33839         this.panels.removeKey(panelId);
33840         return panel;
33841     },
33842     
33843     /**
33844      * Returns the panel specified or null if it's not in this region.
33845      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33846      * @return {Roo.ContentPanel}
33847      */
33848     getPanel : function(id){
33849         if(typeof id == "object"){ // must be panel obj
33850             return id;
33851         }
33852         return this.panels.get(id);
33853     },
33854     
33855     /**
33856      * Returns this regions position (north/south/east/west/center).
33857      * @return {String} 
33858      */
33859     getPosition: function(){
33860         return this.position;    
33861     }
33862 });/*
33863  * Based on:
33864  * Ext JS Library 1.1.1
33865  * Copyright(c) 2006-2007, Ext JS, LLC.
33866  *
33867  * Originally Released Under LGPL - original licence link has changed is not relivant.
33868  *
33869  * Fork - LGPL
33870  * <script type="text/javascript">
33871  */
33872  
33873 /**
33874  * @class Roo.LayoutRegion
33875  * @extends Roo.BasicLayoutRegion
33876  * This class represents a region in a layout manager.
33877  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33878  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33879  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33880  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33881  * @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})
33882  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33883  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33884  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33885  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33886  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33887  * @cfg {String}    title           The title for the region (overrides panel titles)
33888  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33889  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33890  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33891  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33892  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33893  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33894  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33895  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33896  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33897  * @cfg {Boolean}   showPin         True to show a pin button
33898  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33899  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33900  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33901  * @cfg {Number}    width           For East/West panels
33902  * @cfg {Number}    height          For North/South panels
33903  * @cfg {Boolean}   split           To show the splitter
33904  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33905  */
33906 Roo.LayoutRegion = function(mgr, config, pos){
33907     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33908     var dh = Roo.DomHelper;
33909     /** This region's container element 
33910     * @type Roo.Element */
33911     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33912     /** This region's title element 
33913     * @type Roo.Element */
33914
33915     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33916         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33917         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33918     ]}, true);
33919     this.titleEl.enableDisplayMode();
33920     /** This region's title text element 
33921     * @type HTMLElement */
33922     this.titleTextEl = this.titleEl.dom.firstChild;
33923     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33924     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33925     this.closeBtn.enableDisplayMode();
33926     this.closeBtn.on("click", this.closeClicked, this);
33927     this.closeBtn.hide();
33928
33929     this.createBody(config);
33930     this.visible = true;
33931     this.collapsed = false;
33932
33933     if(config.hideWhenEmpty){
33934         this.hide();
33935         this.on("paneladded", this.validateVisibility, this);
33936         this.on("panelremoved", this.validateVisibility, this);
33937     }
33938     this.applyConfig(config);
33939 };
33940
33941 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33942
33943     createBody : function(){
33944         /** This region's body element 
33945         * @type Roo.Element */
33946         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33947     },
33948
33949     applyConfig : function(c){
33950         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33951             var dh = Roo.DomHelper;
33952             if(c.titlebar !== false){
33953                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33954                 this.collapseBtn.on("click", this.collapse, this);
33955                 this.collapseBtn.enableDisplayMode();
33956
33957                 if(c.showPin === true || this.showPin){
33958                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33959                     this.stickBtn.enableDisplayMode();
33960                     this.stickBtn.on("click", this.expand, this);
33961                     this.stickBtn.hide();
33962                 }
33963             }
33964             /** This region's collapsed element
33965             * @type Roo.Element */
33966             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33967                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33968             ]}, true);
33969             if(c.floatable !== false){
33970                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33971                this.collapsedEl.on("click", this.collapseClick, this);
33972             }
33973
33974             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33975                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33976                    id: "message", unselectable: "on", style:{"float":"left"}});
33977                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33978              }
33979             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33980             this.expandBtn.on("click", this.expand, this);
33981         }
33982         if(this.collapseBtn){
33983             this.collapseBtn.setVisible(c.collapsible == true);
33984         }
33985         this.cmargins = c.cmargins || this.cmargins ||
33986                          (this.position == "west" || this.position == "east" ?
33987                              {top: 0, left: 2, right:2, bottom: 0} :
33988                              {top: 2, left: 0, right:0, bottom: 2});
33989         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33990         this.bottomTabs = c.tabPosition != "top";
33991         this.autoScroll = c.autoScroll || false;
33992         if(this.autoScroll){
33993             this.bodyEl.setStyle("overflow", "auto");
33994         }else{
33995             this.bodyEl.setStyle("overflow", "hidden");
33996         }
33997         //if(c.titlebar !== false){
33998             if((!c.titlebar && !c.title) || c.titlebar === false){
33999                 this.titleEl.hide();
34000             }else{
34001                 this.titleEl.show();
34002                 if(c.title){
34003                     this.titleTextEl.innerHTML = c.title;
34004                 }
34005             }
34006         //}
34007         this.duration = c.duration || .30;
34008         this.slideDuration = c.slideDuration || .45;
34009         this.config = c;
34010         if(c.collapsed){
34011             this.collapse(true);
34012         }
34013         if(c.hidden){
34014             this.hide();
34015         }
34016     },
34017     /**
34018      * Returns true if this region is currently visible.
34019      * @return {Boolean}
34020      */
34021     isVisible : function(){
34022         return this.visible;
34023     },
34024
34025     /**
34026      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34027      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34028      */
34029     setCollapsedTitle : function(title){
34030         title = title || "&#160;";
34031         if(this.collapsedTitleTextEl){
34032             this.collapsedTitleTextEl.innerHTML = title;
34033         }
34034     },
34035
34036     getBox : function(){
34037         var b;
34038         if(!this.collapsed){
34039             b = this.el.getBox(false, true);
34040         }else{
34041             b = this.collapsedEl.getBox(false, true);
34042         }
34043         return b;
34044     },
34045
34046     getMargins : function(){
34047         return this.collapsed ? this.cmargins : this.margins;
34048     },
34049
34050     highlight : function(){
34051         this.el.addClass("x-layout-panel-dragover");
34052     },
34053
34054     unhighlight : function(){
34055         this.el.removeClass("x-layout-panel-dragover");
34056     },
34057
34058     updateBox : function(box){
34059         this.box = box;
34060         if(!this.collapsed){
34061             this.el.dom.style.left = box.x + "px";
34062             this.el.dom.style.top = box.y + "px";
34063             this.updateBody(box.width, box.height);
34064         }else{
34065             this.collapsedEl.dom.style.left = box.x + "px";
34066             this.collapsedEl.dom.style.top = box.y + "px";
34067             this.collapsedEl.setSize(box.width, box.height);
34068         }
34069         if(this.tabs){
34070             this.tabs.autoSizeTabs();
34071         }
34072     },
34073
34074     updateBody : function(w, h){
34075         if(w !== null){
34076             this.el.setWidth(w);
34077             w -= this.el.getBorderWidth("rl");
34078             if(this.config.adjustments){
34079                 w += this.config.adjustments[0];
34080             }
34081         }
34082         if(h !== null){
34083             this.el.setHeight(h);
34084             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34085             h -= this.el.getBorderWidth("tb");
34086             if(this.config.adjustments){
34087                 h += this.config.adjustments[1];
34088             }
34089             this.bodyEl.setHeight(h);
34090             if(this.tabs){
34091                 h = this.tabs.syncHeight(h);
34092             }
34093         }
34094         if(this.panelSize){
34095             w = w !== null ? w : this.panelSize.width;
34096             h = h !== null ? h : this.panelSize.height;
34097         }
34098         if(this.activePanel){
34099             var el = this.activePanel.getEl();
34100             w = w !== null ? w : el.getWidth();
34101             h = h !== null ? h : el.getHeight();
34102             this.panelSize = {width: w, height: h};
34103             this.activePanel.setSize(w, h);
34104         }
34105         if(Roo.isIE && this.tabs){
34106             this.tabs.el.repaint();
34107         }
34108     },
34109
34110     /**
34111      * Returns the container element for this region.
34112      * @return {Roo.Element}
34113      */
34114     getEl : function(){
34115         return this.el;
34116     },
34117
34118     /**
34119      * Hides this region.
34120      */
34121     hide : function(){
34122         if(!this.collapsed){
34123             this.el.dom.style.left = "-2000px";
34124             this.el.hide();
34125         }else{
34126             this.collapsedEl.dom.style.left = "-2000px";
34127             this.collapsedEl.hide();
34128         }
34129         this.visible = false;
34130         this.fireEvent("visibilitychange", this, false);
34131     },
34132
34133     /**
34134      * Shows this region if it was previously hidden.
34135      */
34136     show : function(){
34137         if(!this.collapsed){
34138             this.el.show();
34139         }else{
34140             this.collapsedEl.show();
34141         }
34142         this.visible = true;
34143         this.fireEvent("visibilitychange", this, true);
34144     },
34145
34146     closeClicked : function(){
34147         if(this.activePanel){
34148             this.remove(this.activePanel);
34149         }
34150     },
34151
34152     collapseClick : function(e){
34153         if(this.isSlid){
34154            e.stopPropagation();
34155            this.slideIn();
34156         }else{
34157            e.stopPropagation();
34158            this.slideOut();
34159         }
34160     },
34161
34162     /**
34163      * Collapses this region.
34164      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34165      */
34166     collapse : function(skipAnim){
34167         if(this.collapsed) {
34168             return;
34169         }
34170         this.collapsed = true;
34171         if(this.split){
34172             this.split.el.hide();
34173         }
34174         if(this.config.animate && skipAnim !== true){
34175             this.fireEvent("invalidated", this);
34176             this.animateCollapse();
34177         }else{
34178             this.el.setLocation(-20000,-20000);
34179             this.el.hide();
34180             this.collapsedEl.show();
34181             this.fireEvent("collapsed", this);
34182             this.fireEvent("invalidated", this);
34183         }
34184     },
34185
34186     animateCollapse : function(){
34187         // overridden
34188     },
34189
34190     /**
34191      * Expands this region if it was previously collapsed.
34192      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34193      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34194      */
34195     expand : function(e, skipAnim){
34196         if(e) {
34197             e.stopPropagation();
34198         }
34199         if(!this.collapsed || this.el.hasActiveFx()) {
34200             return;
34201         }
34202         if(this.isSlid){
34203             this.afterSlideIn();
34204             skipAnim = true;
34205         }
34206         this.collapsed = false;
34207         if(this.config.animate && skipAnim !== true){
34208             this.animateExpand();
34209         }else{
34210             this.el.show();
34211             if(this.split){
34212                 this.split.el.show();
34213             }
34214             this.collapsedEl.setLocation(-2000,-2000);
34215             this.collapsedEl.hide();
34216             this.fireEvent("invalidated", this);
34217             this.fireEvent("expanded", this);
34218         }
34219     },
34220
34221     animateExpand : function(){
34222         // overridden
34223     },
34224
34225     initTabs : function()
34226     {
34227         this.bodyEl.setStyle("overflow", "hidden");
34228         var ts = new Roo.TabPanel(
34229                 this.bodyEl.dom,
34230                 {
34231                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34232                     disableTooltips: this.config.disableTabTips,
34233                     toolbar : this.config.toolbar
34234                 }
34235         );
34236         if(this.config.hideTabs){
34237             ts.stripWrap.setDisplayed(false);
34238         }
34239         this.tabs = ts;
34240         ts.resizeTabs = this.config.resizeTabs === true;
34241         ts.minTabWidth = this.config.minTabWidth || 40;
34242         ts.maxTabWidth = this.config.maxTabWidth || 250;
34243         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34244         ts.monitorResize = false;
34245         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34246         ts.bodyEl.addClass('x-layout-tabs-body');
34247         this.panels.each(this.initPanelAsTab, this);
34248     },
34249
34250     initPanelAsTab : function(panel){
34251         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34252                     this.config.closeOnTab && panel.isClosable());
34253         if(panel.tabTip !== undefined){
34254             ti.setTooltip(panel.tabTip);
34255         }
34256         ti.on("activate", function(){
34257               this.setActivePanel(panel);
34258         }, this);
34259         if(this.config.closeOnTab){
34260             ti.on("beforeclose", function(t, e){
34261                 e.cancel = true;
34262                 this.remove(panel);
34263             }, this);
34264         }
34265         return ti;
34266     },
34267
34268     updatePanelTitle : function(panel, title){
34269         if(this.activePanel == panel){
34270             this.updateTitle(title);
34271         }
34272         if(this.tabs){
34273             var ti = this.tabs.getTab(panel.getEl().id);
34274             ti.setText(title);
34275             if(panel.tabTip !== undefined){
34276                 ti.setTooltip(panel.tabTip);
34277             }
34278         }
34279     },
34280
34281     updateTitle : function(title){
34282         if(this.titleTextEl && !this.config.title){
34283             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34284         }
34285     },
34286
34287     setActivePanel : function(panel){
34288         panel = this.getPanel(panel);
34289         if(this.activePanel && this.activePanel != panel){
34290             this.activePanel.setActiveState(false);
34291         }
34292         this.activePanel = panel;
34293         panel.setActiveState(true);
34294         if(this.panelSize){
34295             panel.setSize(this.panelSize.width, this.panelSize.height);
34296         }
34297         if(this.closeBtn){
34298             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34299         }
34300         this.updateTitle(panel.getTitle());
34301         if(this.tabs){
34302             this.fireEvent("invalidated", this);
34303         }
34304         this.fireEvent("panelactivated", this, panel);
34305     },
34306
34307     /**
34308      * Shows the specified panel.
34309      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34310      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34311      */
34312     showPanel : function(panel)
34313     {
34314         panel = this.getPanel(panel);
34315         if(panel){
34316             if(this.tabs){
34317                 var tab = this.tabs.getTab(panel.getEl().id);
34318                 if(tab.isHidden()){
34319                     this.tabs.unhideTab(tab.id);
34320                 }
34321                 tab.activate();
34322             }else{
34323                 this.setActivePanel(panel);
34324             }
34325         }
34326         return panel;
34327     },
34328
34329     /**
34330      * Get the active panel for this region.
34331      * @return {Roo.ContentPanel} The active panel or null
34332      */
34333     getActivePanel : function(){
34334         return this.activePanel;
34335     },
34336
34337     validateVisibility : function(){
34338         if(this.panels.getCount() < 1){
34339             this.updateTitle("&#160;");
34340             this.closeBtn.hide();
34341             this.hide();
34342         }else{
34343             if(!this.isVisible()){
34344                 this.show();
34345             }
34346         }
34347     },
34348
34349     /**
34350      * Adds the passed ContentPanel(s) to this region.
34351      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34352      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34353      */
34354     add : function(panel){
34355         if(arguments.length > 1){
34356             for(var i = 0, len = arguments.length; i < len; i++) {
34357                 this.add(arguments[i]);
34358             }
34359             return null;
34360         }
34361         if(this.hasPanel(panel)){
34362             this.showPanel(panel);
34363             return panel;
34364         }
34365         panel.setRegion(this);
34366         this.panels.add(panel);
34367         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34368             this.bodyEl.dom.appendChild(panel.getEl().dom);
34369             if(panel.background !== true){
34370                 this.setActivePanel(panel);
34371             }
34372             this.fireEvent("paneladded", this, panel);
34373             return panel;
34374         }
34375         if(!this.tabs){
34376             this.initTabs();
34377         }else{
34378             this.initPanelAsTab(panel);
34379         }
34380         if(panel.background !== true){
34381             this.tabs.activate(panel.getEl().id);
34382         }
34383         this.fireEvent("paneladded", this, panel);
34384         return panel;
34385     },
34386
34387     /**
34388      * Hides the tab for the specified panel.
34389      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34390      */
34391     hidePanel : function(panel){
34392         if(this.tabs && (panel = this.getPanel(panel))){
34393             this.tabs.hideTab(panel.getEl().id);
34394         }
34395     },
34396
34397     /**
34398      * Unhides the tab for a previously hidden panel.
34399      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34400      */
34401     unhidePanel : function(panel){
34402         if(this.tabs && (panel = this.getPanel(panel))){
34403             this.tabs.unhideTab(panel.getEl().id);
34404         }
34405     },
34406
34407     clearPanels : function(){
34408         while(this.panels.getCount() > 0){
34409              this.remove(this.panels.first());
34410         }
34411     },
34412
34413     /**
34414      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34415      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34416      * @param {Boolean} preservePanel Overrides the config preservePanel option
34417      * @return {Roo.ContentPanel} The panel that was removed
34418      */
34419     remove : function(panel, preservePanel){
34420         panel = this.getPanel(panel);
34421         if(!panel){
34422             return null;
34423         }
34424         var e = {};
34425         this.fireEvent("beforeremove", this, panel, e);
34426         if(e.cancel === true){
34427             return null;
34428         }
34429         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34430         var panelId = panel.getId();
34431         this.panels.removeKey(panelId);
34432         if(preservePanel){
34433             document.body.appendChild(panel.getEl().dom);
34434         }
34435         if(this.tabs){
34436             this.tabs.removeTab(panel.getEl().id);
34437         }else if (!preservePanel){
34438             this.bodyEl.dom.removeChild(panel.getEl().dom);
34439         }
34440         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34441             var p = this.panels.first();
34442             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34443             tempEl.appendChild(p.getEl().dom);
34444             this.bodyEl.update("");
34445             this.bodyEl.dom.appendChild(p.getEl().dom);
34446             tempEl = null;
34447             this.updateTitle(p.getTitle());
34448             this.tabs = null;
34449             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34450             this.setActivePanel(p);
34451         }
34452         panel.setRegion(null);
34453         if(this.activePanel == panel){
34454             this.activePanel = null;
34455         }
34456         if(this.config.autoDestroy !== false && preservePanel !== true){
34457             try{panel.destroy();}catch(e){}
34458         }
34459         this.fireEvent("panelremoved", this, panel);
34460         return panel;
34461     },
34462
34463     /**
34464      * Returns the TabPanel component used by this region
34465      * @return {Roo.TabPanel}
34466      */
34467     getTabs : function(){
34468         return this.tabs;
34469     },
34470
34471     createTool : function(parentEl, className){
34472         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34473             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34474         btn.addClassOnOver("x-layout-tools-button-over");
34475         return btn;
34476     }
34477 });/*
34478  * Based on:
34479  * Ext JS Library 1.1.1
34480  * Copyright(c) 2006-2007, Ext JS, LLC.
34481  *
34482  * Originally Released Under LGPL - original licence link has changed is not relivant.
34483  *
34484  * Fork - LGPL
34485  * <script type="text/javascript">
34486  */
34487  
34488
34489
34490 /**
34491  * @class Roo.SplitLayoutRegion
34492  * @extends Roo.LayoutRegion
34493  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34494  */
34495 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34496     this.cursor = cursor;
34497     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34498 };
34499
34500 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34501     splitTip : "Drag to resize.",
34502     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34503     useSplitTips : false,
34504
34505     applyConfig : function(config){
34506         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34507         if(config.split){
34508             if(!this.split){
34509                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34510                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34511                 /** The SplitBar for this region 
34512                 * @type Roo.SplitBar */
34513                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34514                 this.split.on("moved", this.onSplitMove, this);
34515                 this.split.useShim = config.useShim === true;
34516                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34517                 if(this.useSplitTips){
34518                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34519                 }
34520                 if(config.collapsible){
34521                     this.split.el.on("dblclick", this.collapse,  this);
34522                 }
34523             }
34524             if(typeof config.minSize != "undefined"){
34525                 this.split.minSize = config.minSize;
34526             }
34527             if(typeof config.maxSize != "undefined"){
34528                 this.split.maxSize = config.maxSize;
34529             }
34530             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34531                 this.hideSplitter();
34532             }
34533         }
34534     },
34535
34536     getHMaxSize : function(){
34537          var cmax = this.config.maxSize || 10000;
34538          var center = this.mgr.getRegion("center");
34539          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34540     },
34541
34542     getVMaxSize : function(){
34543          var cmax = this.config.maxSize || 10000;
34544          var center = this.mgr.getRegion("center");
34545          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34546     },
34547
34548     onSplitMove : function(split, newSize){
34549         this.fireEvent("resized", this, newSize);
34550     },
34551     
34552     /** 
34553      * Returns the {@link Roo.SplitBar} for this region.
34554      * @return {Roo.SplitBar}
34555      */
34556     getSplitBar : function(){
34557         return this.split;
34558     },
34559     
34560     hide : function(){
34561         this.hideSplitter();
34562         Roo.SplitLayoutRegion.superclass.hide.call(this);
34563     },
34564
34565     hideSplitter : function(){
34566         if(this.split){
34567             this.split.el.setLocation(-2000,-2000);
34568             this.split.el.hide();
34569         }
34570     },
34571
34572     show : function(){
34573         if(this.split){
34574             this.split.el.show();
34575         }
34576         Roo.SplitLayoutRegion.superclass.show.call(this);
34577     },
34578     
34579     beforeSlide: function(){
34580         if(Roo.isGecko){// firefox overflow auto bug workaround
34581             this.bodyEl.clip();
34582             if(this.tabs) {
34583                 this.tabs.bodyEl.clip();
34584             }
34585             if(this.activePanel){
34586                 this.activePanel.getEl().clip();
34587                 
34588                 if(this.activePanel.beforeSlide){
34589                     this.activePanel.beforeSlide();
34590                 }
34591             }
34592         }
34593     },
34594     
34595     afterSlide : function(){
34596         if(Roo.isGecko){// firefox overflow auto bug workaround
34597             this.bodyEl.unclip();
34598             if(this.tabs) {
34599                 this.tabs.bodyEl.unclip();
34600             }
34601             if(this.activePanel){
34602                 this.activePanel.getEl().unclip();
34603                 if(this.activePanel.afterSlide){
34604                     this.activePanel.afterSlide();
34605                 }
34606             }
34607         }
34608     },
34609
34610     initAutoHide : function(){
34611         if(this.autoHide !== false){
34612             if(!this.autoHideHd){
34613                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34614                 this.autoHideHd = {
34615                     "mouseout": function(e){
34616                         if(!e.within(this.el, true)){
34617                             st.delay(500);
34618                         }
34619                     },
34620                     "mouseover" : function(e){
34621                         st.cancel();
34622                     },
34623                     scope : this
34624                 };
34625             }
34626             this.el.on(this.autoHideHd);
34627         }
34628     },
34629
34630     clearAutoHide : function(){
34631         if(this.autoHide !== false){
34632             this.el.un("mouseout", this.autoHideHd.mouseout);
34633             this.el.un("mouseover", this.autoHideHd.mouseover);
34634         }
34635     },
34636
34637     clearMonitor : function(){
34638         Roo.get(document).un("click", this.slideInIf, this);
34639     },
34640
34641     // these names are backwards but not changed for compat
34642     slideOut : function(){
34643         if(this.isSlid || this.el.hasActiveFx()){
34644             return;
34645         }
34646         this.isSlid = true;
34647         if(this.collapseBtn){
34648             this.collapseBtn.hide();
34649         }
34650         this.closeBtnState = this.closeBtn.getStyle('display');
34651         this.closeBtn.hide();
34652         if(this.stickBtn){
34653             this.stickBtn.show();
34654         }
34655         this.el.show();
34656         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34657         this.beforeSlide();
34658         this.el.setStyle("z-index", 10001);
34659         this.el.slideIn(this.getSlideAnchor(), {
34660             callback: function(){
34661                 this.afterSlide();
34662                 this.initAutoHide();
34663                 Roo.get(document).on("click", this.slideInIf, this);
34664                 this.fireEvent("slideshow", this);
34665             },
34666             scope: this,
34667             block: true
34668         });
34669     },
34670
34671     afterSlideIn : function(){
34672         this.clearAutoHide();
34673         this.isSlid = false;
34674         this.clearMonitor();
34675         this.el.setStyle("z-index", "");
34676         if(this.collapseBtn){
34677             this.collapseBtn.show();
34678         }
34679         this.closeBtn.setStyle('display', this.closeBtnState);
34680         if(this.stickBtn){
34681             this.stickBtn.hide();
34682         }
34683         this.fireEvent("slidehide", this);
34684     },
34685
34686     slideIn : function(cb){
34687         if(!this.isSlid || this.el.hasActiveFx()){
34688             Roo.callback(cb);
34689             return;
34690         }
34691         this.isSlid = false;
34692         this.beforeSlide();
34693         this.el.slideOut(this.getSlideAnchor(), {
34694             callback: function(){
34695                 this.el.setLeftTop(-10000, -10000);
34696                 this.afterSlide();
34697                 this.afterSlideIn();
34698                 Roo.callback(cb);
34699             },
34700             scope: this,
34701             block: true
34702         });
34703     },
34704     
34705     slideInIf : function(e){
34706         if(!e.within(this.el)){
34707             this.slideIn();
34708         }
34709     },
34710
34711     animateCollapse : function(){
34712         this.beforeSlide();
34713         this.el.setStyle("z-index", 20000);
34714         var anchor = this.getSlideAnchor();
34715         this.el.slideOut(anchor, {
34716             callback : function(){
34717                 this.el.setStyle("z-index", "");
34718                 this.collapsedEl.slideIn(anchor, {duration:.3});
34719                 this.afterSlide();
34720                 this.el.setLocation(-10000,-10000);
34721                 this.el.hide();
34722                 this.fireEvent("collapsed", this);
34723             },
34724             scope: this,
34725             block: true
34726         });
34727     },
34728
34729     animateExpand : function(){
34730         this.beforeSlide();
34731         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34732         this.el.setStyle("z-index", 20000);
34733         this.collapsedEl.hide({
34734             duration:.1
34735         });
34736         this.el.slideIn(this.getSlideAnchor(), {
34737             callback : function(){
34738                 this.el.setStyle("z-index", "");
34739                 this.afterSlide();
34740                 if(this.split){
34741                     this.split.el.show();
34742                 }
34743                 this.fireEvent("invalidated", this);
34744                 this.fireEvent("expanded", this);
34745             },
34746             scope: this,
34747             block: true
34748         });
34749     },
34750
34751     anchors : {
34752         "west" : "left",
34753         "east" : "right",
34754         "north" : "top",
34755         "south" : "bottom"
34756     },
34757
34758     sanchors : {
34759         "west" : "l",
34760         "east" : "r",
34761         "north" : "t",
34762         "south" : "b"
34763     },
34764
34765     canchors : {
34766         "west" : "tl-tr",
34767         "east" : "tr-tl",
34768         "north" : "tl-bl",
34769         "south" : "bl-tl"
34770     },
34771
34772     getAnchor : function(){
34773         return this.anchors[this.position];
34774     },
34775
34776     getCollapseAnchor : function(){
34777         return this.canchors[this.position];
34778     },
34779
34780     getSlideAnchor : function(){
34781         return this.sanchors[this.position];
34782     },
34783
34784     getAlignAdj : function(){
34785         var cm = this.cmargins;
34786         switch(this.position){
34787             case "west":
34788                 return [0, 0];
34789             break;
34790             case "east":
34791                 return [0, 0];
34792             break;
34793             case "north":
34794                 return [0, 0];
34795             break;
34796             case "south":
34797                 return [0, 0];
34798             break;
34799         }
34800     },
34801
34802     getExpandAdj : function(){
34803         var c = this.collapsedEl, cm = this.cmargins;
34804         switch(this.position){
34805             case "west":
34806                 return [-(cm.right+c.getWidth()+cm.left), 0];
34807             break;
34808             case "east":
34809                 return [cm.right+c.getWidth()+cm.left, 0];
34810             break;
34811             case "north":
34812                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34813             break;
34814             case "south":
34815                 return [0, cm.top+cm.bottom+c.getHeight()];
34816             break;
34817         }
34818     }
34819 });/*
34820  * Based on:
34821  * Ext JS Library 1.1.1
34822  * Copyright(c) 2006-2007, Ext JS, LLC.
34823  *
34824  * Originally Released Under LGPL - original licence link has changed is not relivant.
34825  *
34826  * Fork - LGPL
34827  * <script type="text/javascript">
34828  */
34829 /*
34830  * These classes are private internal classes
34831  */
34832 Roo.CenterLayoutRegion = function(mgr, config){
34833     Roo.LayoutRegion.call(this, mgr, config, "center");
34834     this.visible = true;
34835     this.minWidth = config.minWidth || 20;
34836     this.minHeight = config.minHeight || 20;
34837 };
34838
34839 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34840     hide : function(){
34841         // center panel can't be hidden
34842     },
34843     
34844     show : function(){
34845         // center panel can't be hidden
34846     },
34847     
34848     getMinWidth: function(){
34849         return this.minWidth;
34850     },
34851     
34852     getMinHeight: function(){
34853         return this.minHeight;
34854     }
34855 });
34856
34857
34858 Roo.NorthLayoutRegion = function(mgr, config){
34859     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34860     if(this.split){
34861         this.split.placement = Roo.SplitBar.TOP;
34862         this.split.orientation = Roo.SplitBar.VERTICAL;
34863         this.split.el.addClass("x-layout-split-v");
34864     }
34865     var size = config.initialSize || config.height;
34866     if(typeof size != "undefined"){
34867         this.el.setHeight(size);
34868     }
34869 };
34870 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34871     orientation: Roo.SplitBar.VERTICAL,
34872     getBox : function(){
34873         if(this.collapsed){
34874             return this.collapsedEl.getBox();
34875         }
34876         var box = this.el.getBox();
34877         if(this.split){
34878             box.height += this.split.el.getHeight();
34879         }
34880         return box;
34881     },
34882     
34883     updateBox : function(box){
34884         if(this.split && !this.collapsed){
34885             box.height -= this.split.el.getHeight();
34886             this.split.el.setLeft(box.x);
34887             this.split.el.setTop(box.y+box.height);
34888             this.split.el.setWidth(box.width);
34889         }
34890         if(this.collapsed){
34891             this.updateBody(box.width, null);
34892         }
34893         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34894     }
34895 });
34896
34897 Roo.SouthLayoutRegion = function(mgr, config){
34898     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34899     if(this.split){
34900         this.split.placement = Roo.SplitBar.BOTTOM;
34901         this.split.orientation = Roo.SplitBar.VERTICAL;
34902         this.split.el.addClass("x-layout-split-v");
34903     }
34904     var size = config.initialSize || config.height;
34905     if(typeof size != "undefined"){
34906         this.el.setHeight(size);
34907     }
34908 };
34909 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34910     orientation: Roo.SplitBar.VERTICAL,
34911     getBox : function(){
34912         if(this.collapsed){
34913             return this.collapsedEl.getBox();
34914         }
34915         var box = this.el.getBox();
34916         if(this.split){
34917             var sh = this.split.el.getHeight();
34918             box.height += sh;
34919             box.y -= sh;
34920         }
34921         return box;
34922     },
34923     
34924     updateBox : function(box){
34925         if(this.split && !this.collapsed){
34926             var sh = this.split.el.getHeight();
34927             box.height -= sh;
34928             box.y += sh;
34929             this.split.el.setLeft(box.x);
34930             this.split.el.setTop(box.y-sh);
34931             this.split.el.setWidth(box.width);
34932         }
34933         if(this.collapsed){
34934             this.updateBody(box.width, null);
34935         }
34936         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34937     }
34938 });
34939
34940 Roo.EastLayoutRegion = function(mgr, config){
34941     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34942     if(this.split){
34943         this.split.placement = Roo.SplitBar.RIGHT;
34944         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34945         this.split.el.addClass("x-layout-split-h");
34946     }
34947     var size = config.initialSize || config.width;
34948     if(typeof size != "undefined"){
34949         this.el.setWidth(size);
34950     }
34951 };
34952 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34953     orientation: Roo.SplitBar.HORIZONTAL,
34954     getBox : function(){
34955         if(this.collapsed){
34956             return this.collapsedEl.getBox();
34957         }
34958         var box = this.el.getBox();
34959         if(this.split){
34960             var sw = this.split.el.getWidth();
34961             box.width += sw;
34962             box.x -= sw;
34963         }
34964         return box;
34965     },
34966
34967     updateBox : function(box){
34968         if(this.split && !this.collapsed){
34969             var sw = this.split.el.getWidth();
34970             box.width -= sw;
34971             this.split.el.setLeft(box.x);
34972             this.split.el.setTop(box.y);
34973             this.split.el.setHeight(box.height);
34974             box.x += sw;
34975         }
34976         if(this.collapsed){
34977             this.updateBody(null, box.height);
34978         }
34979         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34980     }
34981 });
34982
34983 Roo.WestLayoutRegion = function(mgr, config){
34984     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34985     if(this.split){
34986         this.split.placement = Roo.SplitBar.LEFT;
34987         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34988         this.split.el.addClass("x-layout-split-h");
34989     }
34990     var size = config.initialSize || config.width;
34991     if(typeof size != "undefined"){
34992         this.el.setWidth(size);
34993     }
34994 };
34995 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34996     orientation: Roo.SplitBar.HORIZONTAL,
34997     getBox : function(){
34998         if(this.collapsed){
34999             return this.collapsedEl.getBox();
35000         }
35001         var box = this.el.getBox();
35002         if(this.split){
35003             box.width += this.split.el.getWidth();
35004         }
35005         return box;
35006     },
35007     
35008     updateBox : function(box){
35009         if(this.split && !this.collapsed){
35010             var sw = this.split.el.getWidth();
35011             box.width -= sw;
35012             this.split.el.setLeft(box.x+box.width);
35013             this.split.el.setTop(box.y);
35014             this.split.el.setHeight(box.height);
35015         }
35016         if(this.collapsed){
35017             this.updateBody(null, box.height);
35018         }
35019         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35020     }
35021 });
35022 /*
35023  * Based on:
35024  * Ext JS Library 1.1.1
35025  * Copyright(c) 2006-2007, Ext JS, LLC.
35026  *
35027  * Originally Released Under LGPL - original licence link has changed is not relivant.
35028  *
35029  * Fork - LGPL
35030  * <script type="text/javascript">
35031  */
35032  
35033  
35034 /*
35035  * Private internal class for reading and applying state
35036  */
35037 Roo.LayoutStateManager = function(layout){
35038      // default empty state
35039      this.state = {
35040         north: {},
35041         south: {},
35042         east: {},
35043         west: {}       
35044     };
35045 };
35046
35047 Roo.LayoutStateManager.prototype = {
35048     init : function(layout, provider){
35049         this.provider = provider;
35050         var state = provider.get(layout.id+"-layout-state");
35051         if(state){
35052             var wasUpdating = layout.isUpdating();
35053             if(!wasUpdating){
35054                 layout.beginUpdate();
35055             }
35056             for(var key in state){
35057                 if(typeof state[key] != "function"){
35058                     var rstate = state[key];
35059                     var r = layout.getRegion(key);
35060                     if(r && rstate){
35061                         if(rstate.size){
35062                             r.resizeTo(rstate.size);
35063                         }
35064                         if(rstate.collapsed == true){
35065                             r.collapse(true);
35066                         }else{
35067                             r.expand(null, true);
35068                         }
35069                     }
35070                 }
35071             }
35072             if(!wasUpdating){
35073                 layout.endUpdate();
35074             }
35075             this.state = state; 
35076         }
35077         this.layout = layout;
35078         layout.on("regionresized", this.onRegionResized, this);
35079         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35080         layout.on("regionexpanded", this.onRegionExpanded, this);
35081     },
35082     
35083     storeState : function(){
35084         this.provider.set(this.layout.id+"-layout-state", this.state);
35085     },
35086     
35087     onRegionResized : function(region, newSize){
35088         this.state[region.getPosition()].size = newSize;
35089         this.storeState();
35090     },
35091     
35092     onRegionCollapsed : function(region){
35093         this.state[region.getPosition()].collapsed = true;
35094         this.storeState();
35095     },
35096     
35097     onRegionExpanded : function(region){
35098         this.state[region.getPosition()].collapsed = false;
35099         this.storeState();
35100     }
35101 };/*
35102  * Based on:
35103  * Ext JS Library 1.1.1
35104  * Copyright(c) 2006-2007, Ext JS, LLC.
35105  *
35106  * Originally Released Under LGPL - original licence link has changed is not relivant.
35107  *
35108  * Fork - LGPL
35109  * <script type="text/javascript">
35110  */
35111 /**
35112  * @class Roo.ContentPanel
35113  * @extends Roo.util.Observable
35114  * A basic ContentPanel element.
35115  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35116  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35117  * @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
35118  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35119  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35120  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35121  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35122  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35123  * @cfg {String} title          The title for this panel
35124  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35125  * @cfg {String} url            Calls {@link #setUrl} with this value
35126  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35127  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35128  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35129  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35130
35131  * @constructor
35132  * Create a new ContentPanel.
35133  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35134  * @param {String/Object} config A string to set only the title or a config object
35135  * @param {String} content (optional) Set the HTML content for this panel
35136  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35137  */
35138 Roo.ContentPanel = function(el, config, content){
35139     
35140      
35141     /*
35142     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35143         config = el;
35144         el = Roo.id();
35145     }
35146     if (config && config.parentLayout) { 
35147         el = config.parentLayout.el.createChild(); 
35148     }
35149     */
35150     if(el.autoCreate){ // xtype is available if this is called from factory
35151         config = el;
35152         el = Roo.id();
35153     }
35154     this.el = Roo.get(el);
35155     if(!this.el && config && config.autoCreate){
35156         if(typeof config.autoCreate == "object"){
35157             if(!config.autoCreate.id){
35158                 config.autoCreate.id = config.id||el;
35159             }
35160             this.el = Roo.DomHelper.append(document.body,
35161                         config.autoCreate, true);
35162         }else{
35163             this.el = Roo.DomHelper.append(document.body,
35164                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35165         }
35166     }
35167     this.closable = false;
35168     this.loaded = false;
35169     this.active = false;
35170     if(typeof config == "string"){
35171         this.title = config;
35172     }else{
35173         Roo.apply(this, config);
35174     }
35175     
35176     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35177         this.wrapEl = this.el.wrap();
35178         this.toolbar.container = this.el.insertSibling(false, 'before');
35179         this.toolbar = new Roo.Toolbar(this.toolbar);
35180     }
35181     
35182     // xtype created footer. - not sure if will work as we normally have to render first..
35183     if (this.footer && !this.footer.el && this.footer.xtype) {
35184         if (!this.wrapEl) {
35185             this.wrapEl = this.el.wrap();
35186         }
35187     
35188         this.footer.container = this.wrapEl.createChild();
35189          
35190         this.footer = Roo.factory(this.footer, Roo);
35191         
35192     }
35193     
35194     if(this.resizeEl){
35195         this.resizeEl = Roo.get(this.resizeEl, true);
35196     }else{
35197         this.resizeEl = this.el;
35198     }
35199     // handle view.xtype
35200     
35201  
35202     
35203     
35204     this.addEvents({
35205         /**
35206          * @event activate
35207          * Fires when this panel is activated. 
35208          * @param {Roo.ContentPanel} this
35209          */
35210         "activate" : true,
35211         /**
35212          * @event deactivate
35213          * Fires when this panel is activated. 
35214          * @param {Roo.ContentPanel} this
35215          */
35216         "deactivate" : true,
35217
35218         /**
35219          * @event resize
35220          * Fires when this panel is resized if fitToFrame is true.
35221          * @param {Roo.ContentPanel} this
35222          * @param {Number} width The width after any component adjustments
35223          * @param {Number} height The height after any component adjustments
35224          */
35225         "resize" : true,
35226         
35227          /**
35228          * @event render
35229          * Fires when this tab is created
35230          * @param {Roo.ContentPanel} this
35231          */
35232         "render" : true
35233         
35234         
35235         
35236     });
35237     
35238
35239     
35240     
35241     if(this.autoScroll){
35242         this.resizeEl.setStyle("overflow", "auto");
35243     } else {
35244         // fix randome scrolling
35245         this.el.on('scroll', function() {
35246             Roo.log('fix random scolling');
35247             this.scrollTo('top',0); 
35248         });
35249     }
35250     content = content || this.content;
35251     if(content){
35252         this.setContent(content);
35253     }
35254     if(config && config.url){
35255         this.setUrl(this.url, this.params, this.loadOnce);
35256     }
35257     
35258     
35259     
35260     Roo.ContentPanel.superclass.constructor.call(this);
35261     
35262     if (this.view && typeof(this.view.xtype) != 'undefined') {
35263         this.view.el = this.el.appendChild(document.createElement("div"));
35264         this.view = Roo.factory(this.view); 
35265         this.view.render  &&  this.view.render(false, '');  
35266     }
35267     
35268     
35269     this.fireEvent('render', this);
35270 };
35271
35272 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35273     tabTip:'',
35274     setRegion : function(region){
35275         this.region = region;
35276         if(region){
35277            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35278         }else{
35279            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35280         } 
35281     },
35282     
35283     /**
35284      * Returns the toolbar for this Panel if one was configured. 
35285      * @return {Roo.Toolbar} 
35286      */
35287     getToolbar : function(){
35288         return this.toolbar;
35289     },
35290     
35291     setActiveState : function(active){
35292         this.active = active;
35293         if(!active){
35294             this.fireEvent("deactivate", this);
35295         }else{
35296             this.fireEvent("activate", this);
35297         }
35298     },
35299     /**
35300      * Updates this panel's element
35301      * @param {String} content The new content
35302      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35303     */
35304     setContent : function(content, loadScripts){
35305         this.el.update(content, loadScripts);
35306     },
35307
35308     ignoreResize : function(w, h){
35309         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35310             return true;
35311         }else{
35312             this.lastSize = {width: w, height: h};
35313             return false;
35314         }
35315     },
35316     /**
35317      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35318      * @return {Roo.UpdateManager} The UpdateManager
35319      */
35320     getUpdateManager : function(){
35321         return this.el.getUpdateManager();
35322     },
35323      /**
35324      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35325      * @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:
35326 <pre><code>
35327 panel.load({
35328     url: "your-url.php",
35329     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35330     callback: yourFunction,
35331     scope: yourObject, //(optional scope)
35332     discardUrl: false,
35333     nocache: false,
35334     text: "Loading...",
35335     timeout: 30,
35336     scripts: false
35337 });
35338 </code></pre>
35339      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35340      * 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.
35341      * @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}
35342      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35343      * @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.
35344      * @return {Roo.ContentPanel} this
35345      */
35346     load : function(){
35347         var um = this.el.getUpdateManager();
35348         um.update.apply(um, arguments);
35349         return this;
35350     },
35351
35352
35353     /**
35354      * 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.
35355      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35356      * @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)
35357      * @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)
35358      * @return {Roo.UpdateManager} The UpdateManager
35359      */
35360     setUrl : function(url, params, loadOnce){
35361         if(this.refreshDelegate){
35362             this.removeListener("activate", this.refreshDelegate);
35363         }
35364         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35365         this.on("activate", this.refreshDelegate);
35366         return this.el.getUpdateManager();
35367     },
35368     
35369     _handleRefresh : function(url, params, loadOnce){
35370         if(!loadOnce || !this.loaded){
35371             var updater = this.el.getUpdateManager();
35372             updater.update(url, params, this._setLoaded.createDelegate(this));
35373         }
35374     },
35375     
35376     _setLoaded : function(){
35377         this.loaded = true;
35378     }, 
35379     
35380     /**
35381      * Returns this panel's id
35382      * @return {String} 
35383      */
35384     getId : function(){
35385         return this.el.id;
35386     },
35387     
35388     /** 
35389      * Returns this panel's element - used by regiosn to add.
35390      * @return {Roo.Element} 
35391      */
35392     getEl : function(){
35393         return this.wrapEl || this.el;
35394     },
35395     
35396     adjustForComponents : function(width, height)
35397     {
35398         //Roo.log('adjustForComponents ');
35399         if(this.resizeEl != this.el){
35400             width -= this.el.getFrameWidth('lr');
35401             height -= this.el.getFrameWidth('tb');
35402         }
35403         if(this.toolbar){
35404             var te = this.toolbar.getEl();
35405             height -= te.getHeight();
35406             te.setWidth(width);
35407         }
35408         if(this.footer){
35409             var te = this.footer.getEl();
35410             Roo.log("footer:" + te.getHeight());
35411             
35412             height -= te.getHeight();
35413             te.setWidth(width);
35414         }
35415         
35416         
35417         if(this.adjustments){
35418             width += this.adjustments[0];
35419             height += this.adjustments[1];
35420         }
35421         return {"width": width, "height": height};
35422     },
35423     
35424     setSize : function(width, height){
35425         if(this.fitToFrame && !this.ignoreResize(width, height)){
35426             if(this.fitContainer && this.resizeEl != this.el){
35427                 this.el.setSize(width, height);
35428             }
35429             var size = this.adjustForComponents(width, height);
35430             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35431             this.fireEvent('resize', this, size.width, size.height);
35432         }
35433     },
35434     
35435     /**
35436      * Returns this panel's title
35437      * @return {String} 
35438      */
35439     getTitle : function(){
35440         return this.title;
35441     },
35442     
35443     /**
35444      * Set this panel's title
35445      * @param {String} title
35446      */
35447     setTitle : function(title){
35448         this.title = title;
35449         if(this.region){
35450             this.region.updatePanelTitle(this, title);
35451         }
35452     },
35453     
35454     /**
35455      * Returns true is this panel was configured to be closable
35456      * @return {Boolean} 
35457      */
35458     isClosable : function(){
35459         return this.closable;
35460     },
35461     
35462     beforeSlide : function(){
35463         this.el.clip();
35464         this.resizeEl.clip();
35465     },
35466     
35467     afterSlide : function(){
35468         this.el.unclip();
35469         this.resizeEl.unclip();
35470     },
35471     
35472     /**
35473      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35474      *   Will fail silently if the {@link #setUrl} method has not been called.
35475      *   This does not activate the panel, just updates its content.
35476      */
35477     refresh : function(){
35478         if(this.refreshDelegate){
35479            this.loaded = false;
35480            this.refreshDelegate();
35481         }
35482     },
35483     
35484     /**
35485      * Destroys this panel
35486      */
35487     destroy : function(){
35488         this.el.removeAllListeners();
35489         var tempEl = document.createElement("span");
35490         tempEl.appendChild(this.el.dom);
35491         tempEl.innerHTML = "";
35492         this.el.remove();
35493         this.el = null;
35494     },
35495     
35496     /**
35497      * form - if the content panel contains a form - this is a reference to it.
35498      * @type {Roo.form.Form}
35499      */
35500     form : false,
35501     /**
35502      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35503      *    This contains a reference to it.
35504      * @type {Roo.View}
35505      */
35506     view : false,
35507     
35508       /**
35509      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35510      * <pre><code>
35511
35512 layout.addxtype({
35513        xtype : 'Form',
35514        items: [ .... ]
35515    }
35516 );
35517
35518 </code></pre>
35519      * @param {Object} cfg Xtype definition of item to add.
35520      */
35521     
35522     addxtype : function(cfg) {
35523         // add form..
35524         if (cfg.xtype.match(/^Form$/)) {
35525             
35526             var el;
35527             //if (this.footer) {
35528             //    el = this.footer.container.insertSibling(false, 'before');
35529             //} else {
35530                 el = this.el.createChild();
35531             //}
35532
35533             this.form = new  Roo.form.Form(cfg);
35534             
35535             
35536             if ( this.form.allItems.length) {
35537                 this.form.render(el.dom);
35538             }
35539             return this.form;
35540         }
35541         // should only have one of theses..
35542         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35543             // views.. should not be just added - used named prop 'view''
35544             
35545             cfg.el = this.el.appendChild(document.createElement("div"));
35546             // factory?
35547             
35548             var ret = new Roo.factory(cfg);
35549              
35550              ret.render && ret.render(false, ''); // render blank..
35551             this.view = ret;
35552             return ret;
35553         }
35554         return false;
35555     }
35556 });
35557
35558 /**
35559  * @class Roo.GridPanel
35560  * @extends Roo.ContentPanel
35561  * @constructor
35562  * Create a new GridPanel.
35563  * @param {Roo.grid.Grid} grid The grid for this panel
35564  * @param {String/Object} config A string to set only the panel's title, or a config object
35565  */
35566 Roo.GridPanel = function(grid, config){
35567     
35568   
35569     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35570         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35571         
35572     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35573     
35574     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35575     
35576     if(this.toolbar){
35577         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35578     }
35579     // xtype created footer. - not sure if will work as we normally have to render first..
35580     if (this.footer && !this.footer.el && this.footer.xtype) {
35581         
35582         this.footer.container = this.grid.getView().getFooterPanel(true);
35583         this.footer.dataSource = this.grid.dataSource;
35584         this.footer = Roo.factory(this.footer, Roo);
35585         
35586     }
35587     
35588     grid.monitorWindowResize = false; // turn off autosizing
35589     grid.autoHeight = false;
35590     grid.autoWidth = false;
35591     this.grid = grid;
35592     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35593 };
35594
35595 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35596     getId : function(){
35597         return this.grid.id;
35598     },
35599     
35600     /**
35601      * Returns the grid for this panel
35602      * @return {Roo.grid.Grid} 
35603      */
35604     getGrid : function(){
35605         return this.grid;    
35606     },
35607     
35608     setSize : function(width, height){
35609         if(!this.ignoreResize(width, height)){
35610             var grid = this.grid;
35611             var size = this.adjustForComponents(width, height);
35612             grid.getGridEl().setSize(size.width, size.height);
35613             grid.autoSize();
35614         }
35615     },
35616     
35617     beforeSlide : function(){
35618         this.grid.getView().scroller.clip();
35619     },
35620     
35621     afterSlide : function(){
35622         this.grid.getView().scroller.unclip();
35623     },
35624     
35625     destroy : function(){
35626         this.grid.destroy();
35627         delete this.grid;
35628         Roo.GridPanel.superclass.destroy.call(this); 
35629     }
35630 });
35631
35632
35633 /**
35634  * @class Roo.NestedLayoutPanel
35635  * @extends Roo.ContentPanel
35636  * @constructor
35637  * Create a new NestedLayoutPanel.
35638  * 
35639  * 
35640  * @param {Roo.BorderLayout} layout The layout for this panel
35641  * @param {String/Object} config A string to set only the title or a config object
35642  */
35643 Roo.NestedLayoutPanel = function(layout, config)
35644 {
35645     // construct with only one argument..
35646     /* FIXME - implement nicer consturctors
35647     if (layout.layout) {
35648         config = layout;
35649         layout = config.layout;
35650         delete config.layout;
35651     }
35652     if (layout.xtype && !layout.getEl) {
35653         // then layout needs constructing..
35654         layout = Roo.factory(layout, Roo);
35655     }
35656     */
35657     
35658     
35659     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35660     
35661     layout.monitorWindowResize = false; // turn off autosizing
35662     this.layout = layout;
35663     this.layout.getEl().addClass("x-layout-nested-layout");
35664     
35665     
35666     
35667     
35668 };
35669
35670 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35671
35672     setSize : function(width, height){
35673         if(!this.ignoreResize(width, height)){
35674             var size = this.adjustForComponents(width, height);
35675             var el = this.layout.getEl();
35676             el.setSize(size.width, size.height);
35677             var touch = el.dom.offsetWidth;
35678             this.layout.layout();
35679             // ie requires a double layout on the first pass
35680             if(Roo.isIE && !this.initialized){
35681                 this.initialized = true;
35682                 this.layout.layout();
35683             }
35684         }
35685     },
35686     
35687     // activate all subpanels if not currently active..
35688     
35689     setActiveState : function(active){
35690         this.active = active;
35691         if(!active){
35692             this.fireEvent("deactivate", this);
35693             return;
35694         }
35695         
35696         this.fireEvent("activate", this);
35697         // not sure if this should happen before or after..
35698         if (!this.layout) {
35699             return; // should not happen..
35700         }
35701         var reg = false;
35702         for (var r in this.layout.regions) {
35703             reg = this.layout.getRegion(r);
35704             if (reg.getActivePanel()) {
35705                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35706                 reg.setActivePanel(reg.getActivePanel());
35707                 continue;
35708             }
35709             if (!reg.panels.length) {
35710                 continue;
35711             }
35712             reg.showPanel(reg.getPanel(0));
35713         }
35714         
35715         
35716         
35717         
35718     },
35719     
35720     /**
35721      * Returns the nested BorderLayout for this panel
35722      * @return {Roo.BorderLayout} 
35723      */
35724     getLayout : function(){
35725         return this.layout;
35726     },
35727     
35728      /**
35729      * Adds a xtype elements to the layout of the nested panel
35730      * <pre><code>
35731
35732 panel.addxtype({
35733        xtype : 'ContentPanel',
35734        region: 'west',
35735        items: [ .... ]
35736    }
35737 );
35738
35739 panel.addxtype({
35740         xtype : 'NestedLayoutPanel',
35741         region: 'west',
35742         layout: {
35743            center: { },
35744            west: { }   
35745         },
35746         items : [ ... list of content panels or nested layout panels.. ]
35747    }
35748 );
35749 </code></pre>
35750      * @param {Object} cfg Xtype definition of item to add.
35751      */
35752     addxtype : function(cfg) {
35753         return this.layout.addxtype(cfg);
35754     
35755     }
35756 });
35757
35758 Roo.ScrollPanel = function(el, config, content){
35759     config = config || {};
35760     config.fitToFrame = true;
35761     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35762     
35763     this.el.dom.style.overflow = "hidden";
35764     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35765     this.el.removeClass("x-layout-inactive-content");
35766     this.el.on("mousewheel", this.onWheel, this);
35767
35768     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35769     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35770     up.unselectable(); down.unselectable();
35771     up.on("click", this.scrollUp, this);
35772     down.on("click", this.scrollDown, this);
35773     up.addClassOnOver("x-scroller-btn-over");
35774     down.addClassOnOver("x-scroller-btn-over");
35775     up.addClassOnClick("x-scroller-btn-click");
35776     down.addClassOnClick("x-scroller-btn-click");
35777     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35778
35779     this.resizeEl = this.el;
35780     this.el = wrap; this.up = up; this.down = down;
35781 };
35782
35783 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35784     increment : 100,
35785     wheelIncrement : 5,
35786     scrollUp : function(){
35787         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35788     },
35789
35790     scrollDown : function(){
35791         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35792     },
35793
35794     afterScroll : function(){
35795         var el = this.resizeEl;
35796         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35797         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35798         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35799     },
35800
35801     setSize : function(){
35802         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35803         this.afterScroll();
35804     },
35805
35806     onWheel : function(e){
35807         var d = e.getWheelDelta();
35808         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35809         this.afterScroll();
35810         e.stopEvent();
35811     },
35812
35813     setContent : function(content, loadScripts){
35814         this.resizeEl.update(content, loadScripts);
35815     }
35816
35817 });
35818
35819
35820
35821
35822
35823
35824
35825
35826
35827 /**
35828  * @class Roo.TreePanel
35829  * @extends Roo.ContentPanel
35830  * @constructor
35831  * Create a new TreePanel. - defaults to fit/scoll contents.
35832  * @param {String/Object} config A string to set only the panel's title, or a config object
35833  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35834  */
35835 Roo.TreePanel = function(config){
35836     var el = config.el;
35837     var tree = config.tree;
35838     delete config.tree; 
35839     delete config.el; // hopefull!
35840     
35841     // wrapper for IE7 strict & safari scroll issue
35842     
35843     var treeEl = el.createChild();
35844     config.resizeEl = treeEl;
35845     
35846     
35847     
35848     Roo.TreePanel.superclass.constructor.call(this, el, config);
35849  
35850  
35851     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35852     //console.log(tree);
35853     this.on('activate', function()
35854     {
35855         if (this.tree.rendered) {
35856             return;
35857         }
35858         //console.log('render tree');
35859         this.tree.render();
35860     });
35861     // this should not be needed.. - it's actually the 'el' that resizes?
35862     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35863     
35864     //this.on('resize',  function (cp, w, h) {
35865     //        this.tree.innerCt.setWidth(w);
35866     //        this.tree.innerCt.setHeight(h);
35867     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35868     //});
35869
35870         
35871     
35872 };
35873
35874 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35875     fitToFrame : true,
35876     autoScroll : true
35877 });
35878
35879
35880
35881
35882
35883
35884
35885
35886
35887
35888
35889 /*
35890  * Based on:
35891  * Ext JS Library 1.1.1
35892  * Copyright(c) 2006-2007, Ext JS, LLC.
35893  *
35894  * Originally Released Under LGPL - original licence link has changed is not relivant.
35895  *
35896  * Fork - LGPL
35897  * <script type="text/javascript">
35898  */
35899  
35900
35901 /**
35902  * @class Roo.ReaderLayout
35903  * @extends Roo.BorderLayout
35904  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35905  * center region containing two nested regions (a top one for a list view and one for item preview below),
35906  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35907  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35908  * expedites the setup of the overall layout and regions for this common application style.
35909  * Example:
35910  <pre><code>
35911 var reader = new Roo.ReaderLayout();
35912 var CP = Roo.ContentPanel;  // shortcut for adding
35913
35914 reader.beginUpdate();
35915 reader.add("north", new CP("north", "North"));
35916 reader.add("west", new CP("west", {title: "West"}));
35917 reader.add("east", new CP("east", {title: "East"}));
35918
35919 reader.regions.listView.add(new CP("listView", "List"));
35920 reader.regions.preview.add(new CP("preview", "Preview"));
35921 reader.endUpdate();
35922 </code></pre>
35923 * @constructor
35924 * Create a new ReaderLayout
35925 * @param {Object} config Configuration options
35926 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35927 * document.body if omitted)
35928 */
35929 Roo.ReaderLayout = function(config, renderTo){
35930     var c = config || {size:{}};
35931     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35932         north: c.north !== false ? Roo.apply({
35933             split:false,
35934             initialSize: 32,
35935             titlebar: false
35936         }, c.north) : false,
35937         west: c.west !== false ? Roo.apply({
35938             split:true,
35939             initialSize: 200,
35940             minSize: 175,
35941             maxSize: 400,
35942             titlebar: true,
35943             collapsible: true,
35944             animate: true,
35945             margins:{left:5,right:0,bottom:5,top:5},
35946             cmargins:{left:5,right:5,bottom:5,top:5}
35947         }, c.west) : false,
35948         east: c.east !== false ? Roo.apply({
35949             split:true,
35950             initialSize: 200,
35951             minSize: 175,
35952             maxSize: 400,
35953             titlebar: true,
35954             collapsible: true,
35955             animate: true,
35956             margins:{left:0,right:5,bottom:5,top:5},
35957             cmargins:{left:5,right:5,bottom:5,top:5}
35958         }, c.east) : false,
35959         center: Roo.apply({
35960             tabPosition: 'top',
35961             autoScroll:false,
35962             closeOnTab: true,
35963             titlebar:false,
35964             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35965         }, c.center)
35966     });
35967
35968     this.el.addClass('x-reader');
35969
35970     this.beginUpdate();
35971
35972     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35973         south: c.preview !== false ? Roo.apply({
35974             split:true,
35975             initialSize: 200,
35976             minSize: 100,
35977             autoScroll:true,
35978             collapsible:true,
35979             titlebar: true,
35980             cmargins:{top:5,left:0, right:0, bottom:0}
35981         }, c.preview) : false,
35982         center: Roo.apply({
35983             autoScroll:false,
35984             titlebar:false,
35985             minHeight:200
35986         }, c.listView)
35987     });
35988     this.add('center', new Roo.NestedLayoutPanel(inner,
35989             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35990
35991     this.endUpdate();
35992
35993     this.regions.preview = inner.getRegion('south');
35994     this.regions.listView = inner.getRegion('center');
35995 };
35996
35997 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35998  * Based on:
35999  * Ext JS Library 1.1.1
36000  * Copyright(c) 2006-2007, Ext JS, LLC.
36001  *
36002  * Originally Released Under LGPL - original licence link has changed is not relivant.
36003  *
36004  * Fork - LGPL
36005  * <script type="text/javascript">
36006  */
36007  
36008 /**
36009  * @class Roo.grid.Grid
36010  * @extends Roo.util.Observable
36011  * This class represents the primary interface of a component based grid control.
36012  * <br><br>Usage:<pre><code>
36013  var grid = new Roo.grid.Grid("my-container-id", {
36014      ds: myDataStore,
36015      cm: myColModel,
36016      selModel: mySelectionModel,
36017      autoSizeColumns: true,
36018      monitorWindowResize: false,
36019      trackMouseOver: true
36020  });
36021  // set any options
36022  grid.render();
36023  * </code></pre>
36024  * <b>Common Problems:</b><br/>
36025  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36026  * element will correct this<br/>
36027  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36028  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36029  * are unpredictable.<br/>
36030  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36031  * grid to calculate dimensions/offsets.<br/>
36032   * @constructor
36033  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36034  * The container MUST have some type of size defined for the grid to fill. The container will be
36035  * automatically set to position relative if it isn't already.
36036  * @param {Object} config A config object that sets properties on this grid.
36037  */
36038 Roo.grid.Grid = function(container, config){
36039         // initialize the container
36040         this.container = Roo.get(container);
36041         this.container.update("");
36042         this.container.setStyle("overflow", "hidden");
36043     this.container.addClass('x-grid-container');
36044
36045     this.id = this.container.id;
36046
36047     Roo.apply(this, config);
36048     // check and correct shorthanded configs
36049     if(this.ds){
36050         this.dataSource = this.ds;
36051         delete this.ds;
36052     }
36053     if(this.cm){
36054         this.colModel = this.cm;
36055         delete this.cm;
36056     }
36057     if(this.sm){
36058         this.selModel = this.sm;
36059         delete this.sm;
36060     }
36061
36062     if (this.selModel) {
36063         this.selModel = Roo.factory(this.selModel, Roo.grid);
36064         this.sm = this.selModel;
36065         this.sm.xmodule = this.xmodule || false;
36066     }
36067     if (typeof(this.colModel.config) == 'undefined') {
36068         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36069         this.cm = this.colModel;
36070         this.cm.xmodule = this.xmodule || false;
36071     }
36072     if (this.dataSource) {
36073         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36074         this.ds = this.dataSource;
36075         this.ds.xmodule = this.xmodule || false;
36076          
36077     }
36078     
36079     
36080     
36081     if(this.width){
36082         this.container.setWidth(this.width);
36083     }
36084
36085     if(this.height){
36086         this.container.setHeight(this.height);
36087     }
36088     /** @private */
36089         this.addEvents({
36090         // raw events
36091         /**
36092          * @event click
36093          * The raw click event for the entire grid.
36094          * @param {Roo.EventObject} e
36095          */
36096         "click" : true,
36097         /**
36098          * @event dblclick
36099          * The raw dblclick event for the entire grid.
36100          * @param {Roo.EventObject} e
36101          */
36102         "dblclick" : true,
36103         /**
36104          * @event contextmenu
36105          * The raw contextmenu event for the entire grid.
36106          * @param {Roo.EventObject} e
36107          */
36108         "contextmenu" : true,
36109         /**
36110          * @event mousedown
36111          * The raw mousedown event for the entire grid.
36112          * @param {Roo.EventObject} e
36113          */
36114         "mousedown" : true,
36115         /**
36116          * @event mouseup
36117          * The raw mouseup event for the entire grid.
36118          * @param {Roo.EventObject} e
36119          */
36120         "mouseup" : true,
36121         /**
36122          * @event mouseover
36123          * The raw mouseover event for the entire grid.
36124          * @param {Roo.EventObject} e
36125          */
36126         "mouseover" : true,
36127         /**
36128          * @event mouseout
36129          * The raw mouseout event for the entire grid.
36130          * @param {Roo.EventObject} e
36131          */
36132         "mouseout" : true,
36133         /**
36134          * @event keypress
36135          * The raw keypress event for the entire grid.
36136          * @param {Roo.EventObject} e
36137          */
36138         "keypress" : true,
36139         /**
36140          * @event keydown
36141          * The raw keydown event for the entire grid.
36142          * @param {Roo.EventObject} e
36143          */
36144         "keydown" : true,
36145
36146         // custom events
36147
36148         /**
36149          * @event cellclick
36150          * Fires when a cell is clicked
36151          * @param {Grid} this
36152          * @param {Number} rowIndex
36153          * @param {Number} columnIndex
36154          * @param {Roo.EventObject} e
36155          */
36156         "cellclick" : true,
36157         /**
36158          * @event celldblclick
36159          * Fires when a cell is double clicked
36160          * @param {Grid} this
36161          * @param {Number} rowIndex
36162          * @param {Number} columnIndex
36163          * @param {Roo.EventObject} e
36164          */
36165         "celldblclick" : true,
36166         /**
36167          * @event rowclick
36168          * Fires when a row is clicked
36169          * @param {Grid} this
36170          * @param {Number} rowIndex
36171          * @param {Roo.EventObject} e
36172          */
36173         "rowclick" : true,
36174         /**
36175          * @event rowdblclick
36176          * Fires when a row is double clicked
36177          * @param {Grid} this
36178          * @param {Number} rowIndex
36179          * @param {Roo.EventObject} e
36180          */
36181         "rowdblclick" : true,
36182         /**
36183          * @event headerclick
36184          * Fires when a header is clicked
36185          * @param {Grid} this
36186          * @param {Number} columnIndex
36187          * @param {Roo.EventObject} e
36188          */
36189         "headerclick" : true,
36190         /**
36191          * @event headerdblclick
36192          * Fires when a header cell is double clicked
36193          * @param {Grid} this
36194          * @param {Number} columnIndex
36195          * @param {Roo.EventObject} e
36196          */
36197         "headerdblclick" : true,
36198         /**
36199          * @event rowcontextmenu
36200          * Fires when a row is right clicked
36201          * @param {Grid} this
36202          * @param {Number} rowIndex
36203          * @param {Roo.EventObject} e
36204          */
36205         "rowcontextmenu" : true,
36206         /**
36207          * @event cellcontextmenu
36208          * Fires when a cell is right clicked
36209          * @param {Grid} this
36210          * @param {Number} rowIndex
36211          * @param {Number} cellIndex
36212          * @param {Roo.EventObject} e
36213          */
36214          "cellcontextmenu" : true,
36215         /**
36216          * @event headercontextmenu
36217          * Fires when a header is right clicked
36218          * @param {Grid} this
36219          * @param {Number} columnIndex
36220          * @param {Roo.EventObject} e
36221          */
36222         "headercontextmenu" : true,
36223         /**
36224          * @event bodyscroll
36225          * Fires when the body element is scrolled
36226          * @param {Number} scrollLeft
36227          * @param {Number} scrollTop
36228          */
36229         "bodyscroll" : true,
36230         /**
36231          * @event columnresize
36232          * Fires when the user resizes a column
36233          * @param {Number} columnIndex
36234          * @param {Number} newSize
36235          */
36236         "columnresize" : true,
36237         /**
36238          * @event columnmove
36239          * Fires when the user moves a column
36240          * @param {Number} oldIndex
36241          * @param {Number} newIndex
36242          */
36243         "columnmove" : true,
36244         /**
36245          * @event startdrag
36246          * Fires when row(s) start being dragged
36247          * @param {Grid} this
36248          * @param {Roo.GridDD} dd The drag drop object
36249          * @param {event} e The raw browser event
36250          */
36251         "startdrag" : true,
36252         /**
36253          * @event enddrag
36254          * Fires when a drag operation is complete
36255          * @param {Grid} this
36256          * @param {Roo.GridDD} dd The drag drop object
36257          * @param {event} e The raw browser event
36258          */
36259         "enddrag" : true,
36260         /**
36261          * @event dragdrop
36262          * Fires when dragged row(s) are dropped on a valid DD target
36263          * @param {Grid} this
36264          * @param {Roo.GridDD} dd The drag drop object
36265          * @param {String} targetId The target drag drop object
36266          * @param {event} e The raw browser event
36267          */
36268         "dragdrop" : true,
36269         /**
36270          * @event dragover
36271          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36272          * @param {Grid} this
36273          * @param {Roo.GridDD} dd The drag drop object
36274          * @param {String} targetId The target drag drop object
36275          * @param {event} e The raw browser event
36276          */
36277         "dragover" : true,
36278         /**
36279          * @event dragenter
36280          *  Fires when the dragged row(s) first cross another DD target while being dragged
36281          * @param {Grid} this
36282          * @param {Roo.GridDD} dd The drag drop object
36283          * @param {String} targetId The target drag drop object
36284          * @param {event} e The raw browser event
36285          */
36286         "dragenter" : true,
36287         /**
36288          * @event dragout
36289          * Fires when the dragged row(s) leave another DD target while being dragged
36290          * @param {Grid} this
36291          * @param {Roo.GridDD} dd The drag drop object
36292          * @param {String} targetId The target drag drop object
36293          * @param {event} e The raw browser event
36294          */
36295         "dragout" : true,
36296         /**
36297          * @event rowclass
36298          * Fires when a row is rendered, so you can change add a style to it.
36299          * @param {GridView} gridview   The grid view
36300          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36301          */
36302         'rowclass' : true,
36303
36304         /**
36305          * @event render
36306          * Fires when the grid is rendered
36307          * @param {Grid} grid
36308          */
36309         'render' : true
36310     });
36311
36312     Roo.grid.Grid.superclass.constructor.call(this);
36313 };
36314 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36315     
36316     /**
36317      * @cfg {String} ddGroup - drag drop group.
36318      */
36319
36320     /**
36321      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36322      */
36323     minColumnWidth : 25,
36324
36325     /**
36326      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36327      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36328      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36329      */
36330     autoSizeColumns : false,
36331
36332     /**
36333      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36334      */
36335     autoSizeHeaders : true,
36336
36337     /**
36338      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36339      */
36340     monitorWindowResize : true,
36341
36342     /**
36343      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36344      * rows measured to get a columns size. Default is 0 (all rows).
36345      */
36346     maxRowsToMeasure : 0,
36347
36348     /**
36349      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36350      */
36351     trackMouseOver : true,
36352
36353     /**
36354     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36355     */
36356     
36357     /**
36358     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36359     */
36360     enableDragDrop : false,
36361     
36362     /**
36363     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36364     */
36365     enableColumnMove : true,
36366     
36367     /**
36368     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36369     */
36370     enableColumnHide : true,
36371     
36372     /**
36373     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36374     */
36375     enableRowHeightSync : false,
36376     
36377     /**
36378     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36379     */
36380     stripeRows : true,
36381     
36382     /**
36383     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36384     */
36385     autoHeight : false,
36386
36387     /**
36388      * @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.
36389      */
36390     autoExpandColumn : false,
36391
36392     /**
36393     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36394     * Default is 50.
36395     */
36396     autoExpandMin : 50,
36397
36398     /**
36399     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36400     */
36401     autoExpandMax : 1000,
36402
36403     /**
36404     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36405     */
36406     view : null,
36407
36408     /**
36409     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36410     */
36411     loadMask : false,
36412     /**
36413     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36414     */
36415     dropTarget: false,
36416     
36417    
36418     
36419     // private
36420     rendered : false,
36421
36422     /**
36423     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36424     * of a fixed width. Default is false.
36425     */
36426     /**
36427     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36428     */
36429     /**
36430      * Called once after all setup has been completed and the grid is ready to be rendered.
36431      * @return {Roo.grid.Grid} this
36432      */
36433     render : function()
36434     {
36435         var c = this.container;
36436         // try to detect autoHeight/width mode
36437         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36438             this.autoHeight = true;
36439         }
36440         var view = this.getView();
36441         view.init(this);
36442
36443         c.on("click", this.onClick, this);
36444         c.on("dblclick", this.onDblClick, this);
36445         c.on("contextmenu", this.onContextMenu, this);
36446         c.on("keydown", this.onKeyDown, this);
36447         if (Roo.isTouch) {
36448             c.on("touchstart", this.onTouchStart, this);
36449         }
36450
36451         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36452
36453         this.getSelectionModel().init(this);
36454
36455         view.render();
36456
36457         if(this.loadMask){
36458             this.loadMask = new Roo.LoadMask(this.container,
36459                     Roo.apply({store:this.dataSource}, this.loadMask));
36460         }
36461         
36462         
36463         if (this.toolbar && this.toolbar.xtype) {
36464             this.toolbar.container = this.getView().getHeaderPanel(true);
36465             this.toolbar = new Roo.Toolbar(this.toolbar);
36466         }
36467         if (this.footer && this.footer.xtype) {
36468             this.footer.dataSource = this.getDataSource();
36469             this.footer.container = this.getView().getFooterPanel(true);
36470             this.footer = Roo.factory(this.footer, Roo);
36471         }
36472         if (this.dropTarget && this.dropTarget.xtype) {
36473             delete this.dropTarget.xtype;
36474             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36475         }
36476         
36477         
36478         this.rendered = true;
36479         this.fireEvent('render', this);
36480         return this;
36481     },
36482
36483         /**
36484          * Reconfigures the grid to use a different Store and Column Model.
36485          * The View will be bound to the new objects and refreshed.
36486          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36487          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36488          */
36489     reconfigure : function(dataSource, colModel){
36490         if(this.loadMask){
36491             this.loadMask.destroy();
36492             this.loadMask = new Roo.LoadMask(this.container,
36493                     Roo.apply({store:dataSource}, this.loadMask));
36494         }
36495         this.view.bind(dataSource, colModel);
36496         this.dataSource = dataSource;
36497         this.colModel = colModel;
36498         this.view.refresh(true);
36499     },
36500
36501     // private
36502     onKeyDown : function(e){
36503         this.fireEvent("keydown", e);
36504     },
36505
36506     /**
36507      * Destroy this grid.
36508      * @param {Boolean} removeEl True to remove the element
36509      */
36510     destroy : function(removeEl, keepListeners){
36511         if(this.loadMask){
36512             this.loadMask.destroy();
36513         }
36514         var c = this.container;
36515         c.removeAllListeners();
36516         this.view.destroy();
36517         this.colModel.purgeListeners();
36518         if(!keepListeners){
36519             this.purgeListeners();
36520         }
36521         c.update("");
36522         if(removeEl === true){
36523             c.remove();
36524         }
36525     },
36526
36527     // private
36528     processEvent : function(name, e){
36529         // does this fire select???
36530         //Roo.log('grid:processEvent '  + name);
36531         
36532         if (name != 'touchstart' ) {
36533             this.fireEvent(name, e);    
36534         }
36535         
36536         var t = e.getTarget();
36537         var v = this.view;
36538         var header = v.findHeaderIndex(t);
36539         if(header !== false){
36540             var ename = name == 'touchstart' ? 'click' : name;
36541              
36542             this.fireEvent("header" + ename, this, header, e);
36543         }else{
36544             var row = v.findRowIndex(t);
36545             var cell = v.findCellIndex(t);
36546             if (name == 'touchstart') {
36547                 // first touch is always a click.
36548                 // hopefull this happens after selection is updated.?
36549                 name = false;
36550                 
36551                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36552                     var cs = this.selModel.getSelectedCell();
36553                     if (row == cs[0] && cell == cs[1]){
36554                         name = 'dblclick';
36555                     }
36556                 }
36557                 if (typeof(this.selModel.getSelections) != 'undefined') {
36558                     var cs = this.selModel.getSelections();
36559                     var ds = this.dataSource;
36560                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36561                         name = 'dblclick';
36562                     }
36563                 }
36564                 if (!name) {
36565                     return;
36566                 }
36567             }
36568             
36569             
36570             if(row !== false){
36571                 this.fireEvent("row" + name, this, row, e);
36572                 if(cell !== false){
36573                     this.fireEvent("cell" + name, this, row, cell, e);
36574                 }
36575             }
36576         }
36577     },
36578
36579     // private
36580     onClick : function(e){
36581         this.processEvent("click", e);
36582     },
36583    // private
36584     onTouchStart : function(e){
36585         this.processEvent("touchstart", e);
36586     },
36587
36588     // private
36589     onContextMenu : function(e, t){
36590         this.processEvent("contextmenu", e);
36591     },
36592
36593     // private
36594     onDblClick : function(e){
36595         this.processEvent("dblclick", e);
36596     },
36597
36598     // private
36599     walkCells : function(row, col, step, fn, scope){
36600         var cm = this.colModel, clen = cm.getColumnCount();
36601         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36602         if(step < 0){
36603             if(col < 0){
36604                 row--;
36605                 first = false;
36606             }
36607             while(row >= 0){
36608                 if(!first){
36609                     col = clen-1;
36610                 }
36611                 first = false;
36612                 while(col >= 0){
36613                     if(fn.call(scope || this, row, col, cm) === true){
36614                         return [row, col];
36615                     }
36616                     col--;
36617                 }
36618                 row--;
36619             }
36620         } else {
36621             if(col >= clen){
36622                 row++;
36623                 first = false;
36624             }
36625             while(row < rlen){
36626                 if(!first){
36627                     col = 0;
36628                 }
36629                 first = false;
36630                 while(col < clen){
36631                     if(fn.call(scope || this, row, col, cm) === true){
36632                         return [row, col];
36633                     }
36634                     col++;
36635                 }
36636                 row++;
36637             }
36638         }
36639         return null;
36640     },
36641
36642     // private
36643     getSelections : function(){
36644         return this.selModel.getSelections();
36645     },
36646
36647     /**
36648      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36649      * but if manual update is required this method will initiate it.
36650      */
36651     autoSize : function(){
36652         if(this.rendered){
36653             this.view.layout();
36654             if(this.view.adjustForScroll){
36655                 this.view.adjustForScroll();
36656             }
36657         }
36658     },
36659
36660     /**
36661      * Returns the grid's underlying element.
36662      * @return {Element} The element
36663      */
36664     getGridEl : function(){
36665         return this.container;
36666     },
36667
36668     // private for compatibility, overridden by editor grid
36669     stopEditing : function(){},
36670
36671     /**
36672      * Returns the grid's SelectionModel.
36673      * @return {SelectionModel}
36674      */
36675     getSelectionModel : function(){
36676         if(!this.selModel){
36677             this.selModel = new Roo.grid.RowSelectionModel();
36678         }
36679         return this.selModel;
36680     },
36681
36682     /**
36683      * Returns the grid's DataSource.
36684      * @return {DataSource}
36685      */
36686     getDataSource : function(){
36687         return this.dataSource;
36688     },
36689
36690     /**
36691      * Returns the grid's ColumnModel.
36692      * @return {ColumnModel}
36693      */
36694     getColumnModel : function(){
36695         return this.colModel;
36696     },
36697
36698     /**
36699      * Returns the grid's GridView object.
36700      * @return {GridView}
36701      */
36702     getView : function(){
36703         if(!this.view){
36704             this.view = new Roo.grid.GridView(this.viewConfig);
36705         }
36706         return this.view;
36707     },
36708     /**
36709      * Called to get grid's drag proxy text, by default returns this.ddText.
36710      * @return {String}
36711      */
36712     getDragDropText : function(){
36713         var count = this.selModel.getCount();
36714         return String.format(this.ddText, count, count == 1 ? '' : 's');
36715     }
36716 });
36717 /**
36718  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36719  * %0 is replaced with the number of selected rows.
36720  * @type String
36721  */
36722 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36723  * Based on:
36724  * Ext JS Library 1.1.1
36725  * Copyright(c) 2006-2007, Ext JS, LLC.
36726  *
36727  * Originally Released Under LGPL - original licence link has changed is not relivant.
36728  *
36729  * Fork - LGPL
36730  * <script type="text/javascript">
36731  */
36732  
36733 Roo.grid.AbstractGridView = function(){
36734         this.grid = null;
36735         
36736         this.events = {
36737             "beforerowremoved" : true,
36738             "beforerowsinserted" : true,
36739             "beforerefresh" : true,
36740             "rowremoved" : true,
36741             "rowsinserted" : true,
36742             "rowupdated" : true,
36743             "refresh" : true
36744         };
36745     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36746 };
36747
36748 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36749     rowClass : "x-grid-row",
36750     cellClass : "x-grid-cell",
36751     tdClass : "x-grid-td",
36752     hdClass : "x-grid-hd",
36753     splitClass : "x-grid-hd-split",
36754     
36755     init: function(grid){
36756         this.grid = grid;
36757                 var cid = this.grid.getGridEl().id;
36758         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36759         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36760         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36761         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36762         },
36763         
36764     getColumnRenderers : function(){
36765         var renderers = [];
36766         var cm = this.grid.colModel;
36767         var colCount = cm.getColumnCount();
36768         for(var i = 0; i < colCount; i++){
36769             renderers[i] = cm.getRenderer(i);
36770         }
36771         return renderers;
36772     },
36773     
36774     getColumnIds : function(){
36775         var ids = [];
36776         var cm = this.grid.colModel;
36777         var colCount = cm.getColumnCount();
36778         for(var i = 0; i < colCount; i++){
36779             ids[i] = cm.getColumnId(i);
36780         }
36781         return ids;
36782     },
36783     
36784     getDataIndexes : function(){
36785         if(!this.indexMap){
36786             this.indexMap = this.buildIndexMap();
36787         }
36788         return this.indexMap.colToData;
36789     },
36790     
36791     getColumnIndexByDataIndex : function(dataIndex){
36792         if(!this.indexMap){
36793             this.indexMap = this.buildIndexMap();
36794         }
36795         return this.indexMap.dataToCol[dataIndex];
36796     },
36797     
36798     /**
36799      * Set a css style for a column dynamically. 
36800      * @param {Number} colIndex The index of the column
36801      * @param {String} name The css property name
36802      * @param {String} value The css value
36803      */
36804     setCSSStyle : function(colIndex, name, value){
36805         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36806         Roo.util.CSS.updateRule(selector, name, value);
36807     },
36808     
36809     generateRules : function(cm){
36810         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36811         Roo.util.CSS.removeStyleSheet(rulesId);
36812         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36813             var cid = cm.getColumnId(i);
36814             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36815                          this.tdSelector, cid, " {\n}\n",
36816                          this.hdSelector, cid, " {\n}\n",
36817                          this.splitSelector, cid, " {\n}\n");
36818         }
36819         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36820     }
36821 });/*
36822  * Based on:
36823  * Ext JS Library 1.1.1
36824  * Copyright(c) 2006-2007, Ext JS, LLC.
36825  *
36826  * Originally Released Under LGPL - original licence link has changed is not relivant.
36827  *
36828  * Fork - LGPL
36829  * <script type="text/javascript">
36830  */
36831
36832 // private
36833 // This is a support class used internally by the Grid components
36834 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36835     this.grid = grid;
36836     this.view = grid.getView();
36837     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36838     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36839     if(hd2){
36840         this.setHandleElId(Roo.id(hd));
36841         this.setOuterHandleElId(Roo.id(hd2));
36842     }
36843     this.scroll = false;
36844 };
36845 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36846     maxDragWidth: 120,
36847     getDragData : function(e){
36848         var t = Roo.lib.Event.getTarget(e);
36849         var h = this.view.findHeaderCell(t);
36850         if(h){
36851             return {ddel: h.firstChild, header:h};
36852         }
36853         return false;
36854     },
36855
36856     onInitDrag : function(e){
36857         this.view.headersDisabled = true;
36858         var clone = this.dragData.ddel.cloneNode(true);
36859         clone.id = Roo.id();
36860         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36861         this.proxy.update(clone);
36862         return true;
36863     },
36864
36865     afterValidDrop : function(){
36866         var v = this.view;
36867         setTimeout(function(){
36868             v.headersDisabled = false;
36869         }, 50);
36870     },
36871
36872     afterInvalidDrop : function(){
36873         var v = this.view;
36874         setTimeout(function(){
36875             v.headersDisabled = false;
36876         }, 50);
36877     }
36878 });
36879 /*
36880  * Based on:
36881  * Ext JS Library 1.1.1
36882  * Copyright(c) 2006-2007, Ext JS, LLC.
36883  *
36884  * Originally Released Under LGPL - original licence link has changed is not relivant.
36885  *
36886  * Fork - LGPL
36887  * <script type="text/javascript">
36888  */
36889 // private
36890 // This is a support class used internally by the Grid components
36891 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36892     this.grid = grid;
36893     this.view = grid.getView();
36894     // split the proxies so they don't interfere with mouse events
36895     this.proxyTop = Roo.DomHelper.append(document.body, {
36896         cls:"col-move-top", html:"&#160;"
36897     }, true);
36898     this.proxyBottom = Roo.DomHelper.append(document.body, {
36899         cls:"col-move-bottom", html:"&#160;"
36900     }, true);
36901     this.proxyTop.hide = this.proxyBottom.hide = function(){
36902         this.setLeftTop(-100,-100);
36903         this.setStyle("visibility", "hidden");
36904     };
36905     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36906     // temporarily disabled
36907     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36908     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36909 };
36910 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36911     proxyOffsets : [-4, -9],
36912     fly: Roo.Element.fly,
36913
36914     getTargetFromEvent : function(e){
36915         var t = Roo.lib.Event.getTarget(e);
36916         var cindex = this.view.findCellIndex(t);
36917         if(cindex !== false){
36918             return this.view.getHeaderCell(cindex);
36919         }
36920         return null;
36921     },
36922
36923     nextVisible : function(h){
36924         var v = this.view, cm = this.grid.colModel;
36925         h = h.nextSibling;
36926         while(h){
36927             if(!cm.isHidden(v.getCellIndex(h))){
36928                 return h;
36929             }
36930             h = h.nextSibling;
36931         }
36932         return null;
36933     },
36934
36935     prevVisible : function(h){
36936         var v = this.view, cm = this.grid.colModel;
36937         h = h.prevSibling;
36938         while(h){
36939             if(!cm.isHidden(v.getCellIndex(h))){
36940                 return h;
36941             }
36942             h = h.prevSibling;
36943         }
36944         return null;
36945     },
36946
36947     positionIndicator : function(h, n, e){
36948         var x = Roo.lib.Event.getPageX(e);
36949         var r = Roo.lib.Dom.getRegion(n.firstChild);
36950         var px, pt, py = r.top + this.proxyOffsets[1];
36951         if((r.right - x) <= (r.right-r.left)/2){
36952             px = r.right+this.view.borderWidth;
36953             pt = "after";
36954         }else{
36955             px = r.left;
36956             pt = "before";
36957         }
36958         var oldIndex = this.view.getCellIndex(h);
36959         var newIndex = this.view.getCellIndex(n);
36960
36961         if(this.grid.colModel.isFixed(newIndex)){
36962             return false;
36963         }
36964
36965         var locked = this.grid.colModel.isLocked(newIndex);
36966
36967         if(pt == "after"){
36968             newIndex++;
36969         }
36970         if(oldIndex < newIndex){
36971             newIndex--;
36972         }
36973         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36974             return false;
36975         }
36976         px +=  this.proxyOffsets[0];
36977         this.proxyTop.setLeftTop(px, py);
36978         this.proxyTop.show();
36979         if(!this.bottomOffset){
36980             this.bottomOffset = this.view.mainHd.getHeight();
36981         }
36982         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36983         this.proxyBottom.show();
36984         return pt;
36985     },
36986
36987     onNodeEnter : function(n, dd, e, data){
36988         if(data.header != n){
36989             this.positionIndicator(data.header, n, e);
36990         }
36991     },
36992
36993     onNodeOver : function(n, dd, e, data){
36994         var result = false;
36995         if(data.header != n){
36996             result = this.positionIndicator(data.header, n, e);
36997         }
36998         if(!result){
36999             this.proxyTop.hide();
37000             this.proxyBottom.hide();
37001         }
37002         return result ? this.dropAllowed : this.dropNotAllowed;
37003     },
37004
37005     onNodeOut : function(n, dd, e, data){
37006         this.proxyTop.hide();
37007         this.proxyBottom.hide();
37008     },
37009
37010     onNodeDrop : function(n, dd, e, data){
37011         var h = data.header;
37012         if(h != n){
37013             var cm = this.grid.colModel;
37014             var x = Roo.lib.Event.getPageX(e);
37015             var r = Roo.lib.Dom.getRegion(n.firstChild);
37016             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37017             var oldIndex = this.view.getCellIndex(h);
37018             var newIndex = this.view.getCellIndex(n);
37019             var locked = cm.isLocked(newIndex);
37020             if(pt == "after"){
37021                 newIndex++;
37022             }
37023             if(oldIndex < newIndex){
37024                 newIndex--;
37025             }
37026             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37027                 return false;
37028             }
37029             cm.setLocked(oldIndex, locked, true);
37030             cm.moveColumn(oldIndex, newIndex);
37031             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37032             return true;
37033         }
37034         return false;
37035     }
37036 });
37037 /*
37038  * Based on:
37039  * Ext JS Library 1.1.1
37040  * Copyright(c) 2006-2007, Ext JS, LLC.
37041  *
37042  * Originally Released Under LGPL - original licence link has changed is not relivant.
37043  *
37044  * Fork - LGPL
37045  * <script type="text/javascript">
37046  */
37047   
37048 /**
37049  * @class Roo.grid.GridView
37050  * @extends Roo.util.Observable
37051  *
37052  * @constructor
37053  * @param {Object} config
37054  */
37055 Roo.grid.GridView = function(config){
37056     Roo.grid.GridView.superclass.constructor.call(this);
37057     this.el = null;
37058
37059     Roo.apply(this, config);
37060 };
37061
37062 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37063
37064     unselectable :  'unselectable="on"',
37065     unselectableCls :  'x-unselectable',
37066     
37067     
37068     rowClass : "x-grid-row",
37069
37070     cellClass : "x-grid-col",
37071
37072     tdClass : "x-grid-td",
37073
37074     hdClass : "x-grid-hd",
37075
37076     splitClass : "x-grid-split",
37077
37078     sortClasses : ["sort-asc", "sort-desc"],
37079
37080     enableMoveAnim : false,
37081
37082     hlColor: "C3DAF9",
37083
37084     dh : Roo.DomHelper,
37085
37086     fly : Roo.Element.fly,
37087
37088     css : Roo.util.CSS,
37089
37090     borderWidth: 1,
37091
37092     splitOffset: 3,
37093
37094     scrollIncrement : 22,
37095
37096     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37097
37098     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37099
37100     bind : function(ds, cm){
37101         if(this.ds){
37102             this.ds.un("load", this.onLoad, this);
37103             this.ds.un("datachanged", this.onDataChange, this);
37104             this.ds.un("add", this.onAdd, this);
37105             this.ds.un("remove", this.onRemove, this);
37106             this.ds.un("update", this.onUpdate, this);
37107             this.ds.un("clear", this.onClear, this);
37108         }
37109         if(ds){
37110             ds.on("load", this.onLoad, this);
37111             ds.on("datachanged", this.onDataChange, this);
37112             ds.on("add", this.onAdd, this);
37113             ds.on("remove", this.onRemove, this);
37114             ds.on("update", this.onUpdate, this);
37115             ds.on("clear", this.onClear, this);
37116         }
37117         this.ds = ds;
37118
37119         if(this.cm){
37120             this.cm.un("widthchange", this.onColWidthChange, this);
37121             this.cm.un("headerchange", this.onHeaderChange, this);
37122             this.cm.un("hiddenchange", this.onHiddenChange, this);
37123             this.cm.un("columnmoved", this.onColumnMove, this);
37124             this.cm.un("columnlockchange", this.onColumnLock, this);
37125         }
37126         if(cm){
37127             this.generateRules(cm);
37128             cm.on("widthchange", this.onColWidthChange, this);
37129             cm.on("headerchange", this.onHeaderChange, this);
37130             cm.on("hiddenchange", this.onHiddenChange, this);
37131             cm.on("columnmoved", this.onColumnMove, this);
37132             cm.on("columnlockchange", this.onColumnLock, this);
37133         }
37134         this.cm = cm;
37135     },
37136
37137     init: function(grid){
37138         Roo.grid.GridView.superclass.init.call(this, grid);
37139
37140         this.bind(grid.dataSource, grid.colModel);
37141
37142         grid.on("headerclick", this.handleHeaderClick, this);
37143
37144         if(grid.trackMouseOver){
37145             grid.on("mouseover", this.onRowOver, this);
37146             grid.on("mouseout", this.onRowOut, this);
37147         }
37148         grid.cancelTextSelection = function(){};
37149         this.gridId = grid.id;
37150
37151         var tpls = this.templates || {};
37152
37153         if(!tpls.master){
37154             tpls.master = new Roo.Template(
37155                '<div class="x-grid" hidefocus="true">',
37156                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37157                   '<div class="x-grid-topbar"></div>',
37158                   '<div class="x-grid-scroller"><div></div></div>',
37159                   '<div class="x-grid-locked">',
37160                       '<div class="x-grid-header">{lockedHeader}</div>',
37161                       '<div class="x-grid-body">{lockedBody}</div>',
37162                   "</div>",
37163                   '<div class="x-grid-viewport">',
37164                       '<div class="x-grid-header">{header}</div>',
37165                       '<div class="x-grid-body">{body}</div>',
37166                   "</div>",
37167                   '<div class="x-grid-bottombar"></div>',
37168                  
37169                   '<div class="x-grid-resize-proxy">&#160;</div>',
37170                "</div>"
37171             );
37172             tpls.master.disableformats = true;
37173         }
37174
37175         if(!tpls.header){
37176             tpls.header = new Roo.Template(
37177                '<table border="0" cellspacing="0" cellpadding="0">',
37178                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37179                "</table>{splits}"
37180             );
37181             tpls.header.disableformats = true;
37182         }
37183         tpls.header.compile();
37184
37185         if(!tpls.hcell){
37186             tpls.hcell = new Roo.Template(
37187                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div " title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37188                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37189                 "</div></td>"
37190              );
37191              tpls.hcell.disableFormats = true;
37192         }
37193         tpls.hcell.compile();
37194
37195         if(!tpls.hsplit){
37196             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37197                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37198             tpls.hsplit.disableFormats = true;
37199         }
37200         tpls.hsplit.compile();
37201
37202         if(!tpls.body){
37203             tpls.body = new Roo.Template(
37204                '<table border="0" cellspacing="0" cellpadding="0">',
37205                "<tbody>{rows}</tbody>",
37206                "</table>"
37207             );
37208             tpls.body.disableFormats = true;
37209         }
37210         tpls.body.compile();
37211
37212         if(!tpls.row){
37213             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37214             tpls.row.disableFormats = true;
37215         }
37216         tpls.row.compile();
37217
37218         if(!tpls.cell){
37219             tpls.cell = new Roo.Template(
37220                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37221                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37222                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37223                 "</td>"
37224             );
37225             tpls.cell.disableFormats = true;
37226         }
37227         tpls.cell.compile();
37228
37229         this.templates = tpls;
37230     },
37231
37232     // remap these for backwards compat
37233     onColWidthChange : function(){
37234         this.updateColumns.apply(this, arguments);
37235     },
37236     onHeaderChange : function(){
37237         this.updateHeaders.apply(this, arguments);
37238     }, 
37239     onHiddenChange : function(){
37240         this.handleHiddenChange.apply(this, arguments);
37241     },
37242     onColumnMove : function(){
37243         this.handleColumnMove.apply(this, arguments);
37244     },
37245     onColumnLock : function(){
37246         this.handleLockChange.apply(this, arguments);
37247     },
37248
37249     onDataChange : function(){
37250         this.refresh();
37251         this.updateHeaderSortState();
37252     },
37253
37254     onClear : function(){
37255         this.refresh();
37256     },
37257
37258     onUpdate : function(ds, record){
37259         this.refreshRow(record);
37260     },
37261
37262     refreshRow : function(record){
37263         var ds = this.ds, index;
37264         if(typeof record == 'number'){
37265             index = record;
37266             record = ds.getAt(index);
37267         }else{
37268             index = ds.indexOf(record);
37269         }
37270         this.insertRows(ds, index, index, true);
37271         this.onRemove(ds, record, index+1, true);
37272         this.syncRowHeights(index, index);
37273         this.layout();
37274         this.fireEvent("rowupdated", this, index, record);
37275     },
37276
37277     onAdd : function(ds, records, index){
37278         this.insertRows(ds, index, index + (records.length-1));
37279     },
37280
37281     onRemove : function(ds, record, index, isUpdate){
37282         if(isUpdate !== true){
37283             this.fireEvent("beforerowremoved", this, index, record);
37284         }
37285         var bt = this.getBodyTable(), lt = this.getLockedTable();
37286         if(bt.rows[index]){
37287             bt.firstChild.removeChild(bt.rows[index]);
37288         }
37289         if(lt.rows[index]){
37290             lt.firstChild.removeChild(lt.rows[index]);
37291         }
37292         if(isUpdate !== true){
37293             this.stripeRows(index);
37294             this.syncRowHeights(index, index);
37295             this.layout();
37296             this.fireEvent("rowremoved", this, index, record);
37297         }
37298     },
37299
37300     onLoad : function(){
37301         this.scrollToTop();
37302     },
37303
37304     /**
37305      * Scrolls the grid to the top
37306      */
37307     scrollToTop : function(){
37308         if(this.scroller){
37309             this.scroller.dom.scrollTop = 0;
37310             this.syncScroll();
37311         }
37312     },
37313
37314     /**
37315      * Gets a panel in the header of the grid that can be used for toolbars etc.
37316      * After modifying the contents of this panel a call to grid.autoSize() may be
37317      * required to register any changes in size.
37318      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37319      * @return Roo.Element
37320      */
37321     getHeaderPanel : function(doShow){
37322         if(doShow){
37323             this.headerPanel.show();
37324         }
37325         return this.headerPanel;
37326     },
37327
37328     /**
37329      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37330      * After modifying the contents of this panel a call to grid.autoSize() may be
37331      * required to register any changes in size.
37332      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37333      * @return Roo.Element
37334      */
37335     getFooterPanel : function(doShow){
37336         if(doShow){
37337             this.footerPanel.show();
37338         }
37339         return this.footerPanel;
37340     },
37341
37342     initElements : function(){
37343         var E = Roo.Element;
37344         var el = this.grid.getGridEl().dom.firstChild;
37345         var cs = el.childNodes;
37346
37347         this.el = new E(el);
37348         
37349          this.focusEl = new E(el.firstChild);
37350         this.focusEl.swallowEvent("click", true);
37351         
37352         this.headerPanel = new E(cs[1]);
37353         this.headerPanel.enableDisplayMode("block");
37354
37355         this.scroller = new E(cs[2]);
37356         this.scrollSizer = new E(this.scroller.dom.firstChild);
37357
37358         this.lockedWrap = new E(cs[3]);
37359         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37360         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37361
37362         this.mainWrap = new E(cs[4]);
37363         this.mainHd = new E(this.mainWrap.dom.firstChild);
37364         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37365
37366         this.footerPanel = new E(cs[5]);
37367         this.footerPanel.enableDisplayMode("block");
37368
37369         this.resizeProxy = new E(cs[6]);
37370
37371         this.headerSelector = String.format(
37372            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37373            this.lockedHd.id, this.mainHd.id
37374         );
37375
37376         this.splitterSelector = String.format(
37377            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37378            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37379         );
37380     },
37381     idToCssName : function(s)
37382     {
37383         return s.replace(/[^a-z0-9]+/ig, '-');
37384     },
37385
37386     getHeaderCell : function(index){
37387         return Roo.DomQuery.select(this.headerSelector)[index];
37388     },
37389
37390     getHeaderCellMeasure : function(index){
37391         return this.getHeaderCell(index).firstChild;
37392     },
37393
37394     getHeaderCellText : function(index){
37395         return this.getHeaderCell(index).firstChild.firstChild;
37396     },
37397
37398     getLockedTable : function(){
37399         return this.lockedBody.dom.firstChild;
37400     },
37401
37402     getBodyTable : function(){
37403         return this.mainBody.dom.firstChild;
37404     },
37405
37406     getLockedRow : function(index){
37407         return this.getLockedTable().rows[index];
37408     },
37409
37410     getRow : function(index){
37411         return this.getBodyTable().rows[index];
37412     },
37413
37414     getRowComposite : function(index){
37415         if(!this.rowEl){
37416             this.rowEl = new Roo.CompositeElementLite();
37417         }
37418         var els = [], lrow, mrow;
37419         if(lrow = this.getLockedRow(index)){
37420             els.push(lrow);
37421         }
37422         if(mrow = this.getRow(index)){
37423             els.push(mrow);
37424         }
37425         this.rowEl.elements = els;
37426         return this.rowEl;
37427     },
37428     /**
37429      * Gets the 'td' of the cell
37430      * 
37431      * @param {Integer} rowIndex row to select
37432      * @param {Integer} colIndex column to select
37433      * 
37434      * @return {Object} 
37435      */
37436     getCell : function(rowIndex, colIndex){
37437         var locked = this.cm.getLockedCount();
37438         var source;
37439         if(colIndex < locked){
37440             source = this.lockedBody.dom.firstChild;
37441         }else{
37442             source = this.mainBody.dom.firstChild;
37443             colIndex -= locked;
37444         }
37445         return source.rows[rowIndex].childNodes[colIndex];
37446     },
37447
37448     getCellText : function(rowIndex, colIndex){
37449         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37450     },
37451
37452     getCellBox : function(cell){
37453         var b = this.fly(cell).getBox();
37454         if(Roo.isOpera){ // opera fails to report the Y
37455             b.y = cell.offsetTop + this.mainBody.getY();
37456         }
37457         return b;
37458     },
37459
37460     getCellIndex : function(cell){
37461         var id = String(cell.className).match(this.cellRE);
37462         if(id){
37463             return parseInt(id[1], 10);
37464         }
37465         return 0;
37466     },
37467
37468     findHeaderIndex : function(n){
37469         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37470         return r ? this.getCellIndex(r) : false;
37471     },
37472
37473     findHeaderCell : function(n){
37474         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37475         return r ? r : false;
37476     },
37477
37478     findRowIndex : function(n){
37479         if(!n){
37480             return false;
37481         }
37482         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37483         return r ? r.rowIndex : false;
37484     },
37485
37486     findCellIndex : function(node){
37487         var stop = this.el.dom;
37488         while(node && node != stop){
37489             if(this.findRE.test(node.className)){
37490                 return this.getCellIndex(node);
37491             }
37492             node = node.parentNode;
37493         }
37494         return false;
37495     },
37496
37497     getColumnId : function(index){
37498         return this.cm.getColumnId(index);
37499     },
37500
37501     getSplitters : function()
37502     {
37503         if(this.splitterSelector){
37504            return Roo.DomQuery.select(this.splitterSelector);
37505         }else{
37506             return null;
37507       }
37508     },
37509
37510     getSplitter : function(index){
37511         return this.getSplitters()[index];
37512     },
37513
37514     onRowOver : function(e, t){
37515         var row;
37516         if((row = this.findRowIndex(t)) !== false){
37517             this.getRowComposite(row).addClass("x-grid-row-over");
37518         }
37519     },
37520
37521     onRowOut : function(e, t){
37522         var row;
37523         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37524             this.getRowComposite(row).removeClass("x-grid-row-over");
37525         }
37526     },
37527
37528     renderHeaders : function(){
37529         var cm = this.cm;
37530         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37531         var cb = [], lb = [], sb = [], lsb = [], p = {};
37532         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37533             p.cellId = "x-grid-hd-0-" + i;
37534             p.splitId = "x-grid-csplit-0-" + i;
37535             p.id = cm.getColumnId(i);
37536             p.title = cm.getColumnTooltip(i) || cm.getColumnHeader(i) || "";
37537             p.value = cm.getColumnHeader(i) || "";
37538             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37539             if(!cm.isLocked(i)){
37540                 cb[cb.length] = ct.apply(p);
37541                 sb[sb.length] = st.apply(p);
37542             }else{
37543                 lb[lb.length] = ct.apply(p);
37544                 lsb[lsb.length] = st.apply(p);
37545             }
37546         }
37547         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37548                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37549     },
37550
37551     updateHeaders : function(){
37552         var html = this.renderHeaders();
37553         this.lockedHd.update(html[0]);
37554         this.mainHd.update(html[1]);
37555     },
37556
37557     /**
37558      * Focuses the specified row.
37559      * @param {Number} row The row index
37560      */
37561     focusRow : function(row)
37562     {
37563         //Roo.log('GridView.focusRow');
37564         var x = this.scroller.dom.scrollLeft;
37565         this.focusCell(row, 0, false);
37566         this.scroller.dom.scrollLeft = x;
37567     },
37568
37569     /**
37570      * Focuses the specified cell.
37571      * @param {Number} row The row index
37572      * @param {Number} col The column index
37573      * @param {Boolean} hscroll false to disable horizontal scrolling
37574      */
37575     focusCell : function(row, col, hscroll)
37576     {
37577         //Roo.log('GridView.focusCell');
37578         var el = this.ensureVisible(row, col, hscroll);
37579         this.focusEl.alignTo(el, "tl-tl");
37580         if(Roo.isGecko){
37581             this.focusEl.focus();
37582         }else{
37583             this.focusEl.focus.defer(1, this.focusEl);
37584         }
37585     },
37586
37587     /**
37588      * Scrolls the specified cell into view
37589      * @param {Number} row The row index
37590      * @param {Number} col The column index
37591      * @param {Boolean} hscroll false to disable horizontal scrolling
37592      */
37593     ensureVisible : function(row, col, hscroll)
37594     {
37595         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37596         //return null; //disable for testing.
37597         if(typeof row != "number"){
37598             row = row.rowIndex;
37599         }
37600         if(row < 0 && row >= this.ds.getCount()){
37601             return  null;
37602         }
37603         col = (col !== undefined ? col : 0);
37604         var cm = this.grid.colModel;
37605         while(cm.isHidden(col)){
37606             col++;
37607         }
37608
37609         var el = this.getCell(row, col);
37610         if(!el){
37611             return null;
37612         }
37613         var c = this.scroller.dom;
37614
37615         var ctop = parseInt(el.offsetTop, 10);
37616         var cleft = parseInt(el.offsetLeft, 10);
37617         var cbot = ctop + el.offsetHeight;
37618         var cright = cleft + el.offsetWidth;
37619         
37620         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37621         var stop = parseInt(c.scrollTop, 10);
37622         var sleft = parseInt(c.scrollLeft, 10);
37623         var sbot = stop + ch;
37624         var sright = sleft + c.clientWidth;
37625         /*
37626         Roo.log('GridView.ensureVisible:' +
37627                 ' ctop:' + ctop +
37628                 ' c.clientHeight:' + c.clientHeight +
37629                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37630                 ' stop:' + stop +
37631                 ' cbot:' + cbot +
37632                 ' sbot:' + sbot +
37633                 ' ch:' + ch  
37634                 );
37635         */
37636         if(ctop < stop){
37637              c.scrollTop = ctop;
37638             //Roo.log("set scrolltop to ctop DISABLE?");
37639         }else if(cbot > sbot){
37640             //Roo.log("set scrolltop to cbot-ch");
37641             c.scrollTop = cbot-ch;
37642         }
37643         
37644         if(hscroll !== false){
37645             if(cleft < sleft){
37646                 c.scrollLeft = cleft;
37647             }else if(cright > sright){
37648                 c.scrollLeft = cright-c.clientWidth;
37649             }
37650         }
37651          
37652         return el;
37653     },
37654
37655     updateColumns : function(){
37656         this.grid.stopEditing();
37657         var cm = this.grid.colModel, colIds = this.getColumnIds();
37658         //var totalWidth = cm.getTotalWidth();
37659         var pos = 0;
37660         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37661             //if(cm.isHidden(i)) continue;
37662             var w = cm.getColumnWidth(i);
37663             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37664             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37665         }
37666         this.updateSplitters();
37667     },
37668
37669     generateRules : function(cm){
37670         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37671         Roo.util.CSS.removeStyleSheet(rulesId);
37672         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37673             var cid = cm.getColumnId(i);
37674             var align = '';
37675             if(cm.config[i].align){
37676                 align = 'text-align:'+cm.config[i].align+';';
37677             }
37678             var hidden = '';
37679             if(cm.isHidden(i)){
37680                 hidden = 'display:none;';
37681             }
37682             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37683             ruleBuf.push(
37684                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37685                     this.hdSelector, cid, " {\n", align, width, "}\n",
37686                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37687                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37688         }
37689         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37690     },
37691
37692     updateSplitters : function(){
37693         var cm = this.cm, s = this.getSplitters();
37694         if(s){ // splitters not created yet
37695             var pos = 0, locked = true;
37696             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37697                 if(cm.isHidden(i)) {
37698                     continue;
37699                 }
37700                 var w = cm.getColumnWidth(i); // make sure it's a number
37701                 if(!cm.isLocked(i) && locked){
37702                     pos = 0;
37703                     locked = false;
37704                 }
37705                 pos += w;
37706                 s[i].style.left = (pos-this.splitOffset) + "px";
37707             }
37708         }
37709     },
37710
37711     handleHiddenChange : function(colModel, colIndex, hidden){
37712         if(hidden){
37713             this.hideColumn(colIndex);
37714         }else{
37715             this.unhideColumn(colIndex);
37716         }
37717     },
37718
37719     hideColumn : function(colIndex){
37720         var cid = this.getColumnId(colIndex);
37721         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37722         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37723         if(Roo.isSafari){
37724             this.updateHeaders();
37725         }
37726         this.updateSplitters();
37727         this.layout();
37728     },
37729
37730     unhideColumn : function(colIndex){
37731         var cid = this.getColumnId(colIndex);
37732         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37733         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37734
37735         if(Roo.isSafari){
37736             this.updateHeaders();
37737         }
37738         this.updateSplitters();
37739         this.layout();
37740     },
37741
37742     insertRows : function(dm, firstRow, lastRow, isUpdate){
37743         if(firstRow == 0 && lastRow == dm.getCount()-1){
37744             this.refresh();
37745         }else{
37746             if(!isUpdate){
37747                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37748             }
37749             var s = this.getScrollState();
37750             var markup = this.renderRows(firstRow, lastRow);
37751             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37752             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37753             this.restoreScroll(s);
37754             if(!isUpdate){
37755                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37756                 this.syncRowHeights(firstRow, lastRow);
37757                 this.stripeRows(firstRow);
37758                 this.layout();
37759             }
37760         }
37761     },
37762
37763     bufferRows : function(markup, target, index){
37764         var before = null, trows = target.rows, tbody = target.tBodies[0];
37765         if(index < trows.length){
37766             before = trows[index];
37767         }
37768         var b = document.createElement("div");
37769         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37770         var rows = b.firstChild.rows;
37771         for(var i = 0, len = rows.length; i < len; i++){
37772             if(before){
37773                 tbody.insertBefore(rows[0], before);
37774             }else{
37775                 tbody.appendChild(rows[0]);
37776             }
37777         }
37778         b.innerHTML = "";
37779         b = null;
37780     },
37781
37782     deleteRows : function(dm, firstRow, lastRow){
37783         if(dm.getRowCount()<1){
37784             this.fireEvent("beforerefresh", this);
37785             this.mainBody.update("");
37786             this.lockedBody.update("");
37787             this.fireEvent("refresh", this);
37788         }else{
37789             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37790             var bt = this.getBodyTable();
37791             var tbody = bt.firstChild;
37792             var rows = bt.rows;
37793             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37794                 tbody.removeChild(rows[firstRow]);
37795             }
37796             this.stripeRows(firstRow);
37797             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37798         }
37799     },
37800
37801     updateRows : function(dataSource, firstRow, lastRow){
37802         var s = this.getScrollState();
37803         this.refresh();
37804         this.restoreScroll(s);
37805     },
37806
37807     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37808         if(!noRefresh){
37809            this.refresh();
37810         }
37811         this.updateHeaderSortState();
37812     },
37813
37814     getScrollState : function(){
37815         
37816         var sb = this.scroller.dom;
37817         return {left: sb.scrollLeft, top: sb.scrollTop};
37818     },
37819
37820     stripeRows : function(startRow){
37821         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37822             return;
37823         }
37824         startRow = startRow || 0;
37825         var rows = this.getBodyTable().rows;
37826         var lrows = this.getLockedTable().rows;
37827         var cls = ' x-grid-row-alt ';
37828         for(var i = startRow, len = rows.length; i < len; i++){
37829             var row = rows[i], lrow = lrows[i];
37830             var isAlt = ((i+1) % 2 == 0);
37831             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37832             if(isAlt == hasAlt){
37833                 continue;
37834             }
37835             if(isAlt){
37836                 row.className += " x-grid-row-alt";
37837             }else{
37838                 row.className = row.className.replace("x-grid-row-alt", "");
37839             }
37840             if(lrow){
37841                 lrow.className = row.className;
37842             }
37843         }
37844     },
37845
37846     restoreScroll : function(state){
37847         //Roo.log('GridView.restoreScroll');
37848         var sb = this.scroller.dom;
37849         sb.scrollLeft = state.left;
37850         sb.scrollTop = state.top;
37851         this.syncScroll();
37852     },
37853
37854     syncScroll : function(){
37855         //Roo.log('GridView.syncScroll');
37856         var sb = this.scroller.dom;
37857         var sh = this.mainHd.dom;
37858         var bs = this.mainBody.dom;
37859         var lv = this.lockedBody.dom;
37860         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37861         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37862     },
37863
37864     handleScroll : function(e){
37865         this.syncScroll();
37866         var sb = this.scroller.dom;
37867         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37868         e.stopEvent();
37869     },
37870
37871     handleWheel : function(e){
37872         var d = e.getWheelDelta();
37873         this.scroller.dom.scrollTop -= d*22;
37874         // set this here to prevent jumpy scrolling on large tables
37875         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37876         e.stopEvent();
37877     },
37878
37879     renderRows : function(startRow, endRow){
37880         // pull in all the crap needed to render rows
37881         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37882         var colCount = cm.getColumnCount();
37883
37884         if(ds.getCount() < 1){
37885             return ["", ""];
37886         }
37887
37888         // build a map for all the columns
37889         var cs = [];
37890         for(var i = 0; i < colCount; i++){
37891             var name = cm.getDataIndex(i);
37892             cs[i] = {
37893                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37894                 renderer : cm.getRenderer(i),
37895                 id : cm.getColumnId(i),
37896                 locked : cm.isLocked(i),
37897                 has_editor : cm.isCellEditable(i)
37898             };
37899         }
37900
37901         startRow = startRow || 0;
37902         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37903
37904         // records to render
37905         var rs = ds.getRange(startRow, endRow);
37906
37907         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37908     },
37909
37910     // As much as I hate to duplicate code, this was branched because FireFox really hates
37911     // [].join("") on strings. The performance difference was substantial enough to
37912     // branch this function
37913     doRender : Roo.isGecko ?
37914             function(cs, rs, ds, startRow, colCount, stripe){
37915                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37916                 // buffers
37917                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37918                 
37919                 var hasListener = this.grid.hasListener('rowclass');
37920                 var rowcfg = {};
37921                 for(var j = 0, len = rs.length; j < len; j++){
37922                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37923                     for(var i = 0; i < colCount; i++){
37924                         c = cs[i];
37925                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37926                         p.id = c.id;
37927                         p.css = p.attr = "";
37928                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37929                         if(p.value == undefined || p.value === "") {
37930                             p.value = "&#160;";
37931                         }
37932                         if(c.has_editor){
37933                             p.css += ' x-grid-editable-cell';
37934                         }
37935                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37936                             p.css +=  ' x-grid-dirty-cell';
37937                         }
37938                         var markup = ct.apply(p);
37939                         if(!c.locked){
37940                             cb+= markup;
37941                         }else{
37942                             lcb+= markup;
37943                         }
37944                     }
37945                     var alt = [];
37946                     if(stripe && ((rowIndex+1) % 2 == 0)){
37947                         alt.push("x-grid-row-alt")
37948                     }
37949                     if(r.dirty){
37950                         alt.push(  " x-grid-dirty-row");
37951                     }
37952                     rp.cells = lcb;
37953                     if(this.getRowClass){
37954                         alt.push(this.getRowClass(r, rowIndex));
37955                     }
37956                     if (hasListener) {
37957                         rowcfg = {
37958                              
37959                             record: r,
37960                             rowIndex : rowIndex,
37961                             rowClass : ''
37962                         };
37963                         this.grid.fireEvent('rowclass', this, rowcfg);
37964                         alt.push(rowcfg.rowClass);
37965                     }
37966                     rp.alt = alt.join(" ");
37967                     lbuf+= rt.apply(rp);
37968                     rp.cells = cb;
37969                     buf+=  rt.apply(rp);
37970                 }
37971                 return [lbuf, buf];
37972             } :
37973             function(cs, rs, ds, startRow, colCount, stripe){
37974                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37975                 // buffers
37976                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37977                 var hasListener = this.grid.hasListener('rowclass');
37978  
37979                 var rowcfg = {};
37980                 for(var j = 0, len = rs.length; j < len; j++){
37981                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37982                     for(var i = 0; i < colCount; i++){
37983                         c = cs[i];
37984                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37985                         p.id = c.id;
37986                         p.css = p.attr = "";
37987                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37988                         if(p.value == undefined || p.value === "") {
37989                             p.value = "&#160;";
37990                         }
37991                         //Roo.log(c);
37992                          if(c.has_editor){
37993                             p.css += ' x-grid-editable-cell';
37994                         }
37995                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37996                             p.css += ' x-grid-dirty-cell' 
37997                         }
37998                         
37999                         var markup = ct.apply(p);
38000                         if(!c.locked){
38001                             cb[cb.length] = markup;
38002                         }else{
38003                             lcb[lcb.length] = markup;
38004                         }
38005                     }
38006                     var alt = [];
38007                     if(stripe && ((rowIndex+1) % 2 == 0)){
38008                         alt.push( "x-grid-row-alt");
38009                     }
38010                     if(r.dirty){
38011                         alt.push(" x-grid-dirty-row");
38012                     }
38013                     rp.cells = lcb;
38014                     if(this.getRowClass){
38015                         alt.push( this.getRowClass(r, rowIndex));
38016                     }
38017                     if (hasListener) {
38018                         rowcfg = {
38019                              
38020                             record: r,
38021                             rowIndex : rowIndex,
38022                             rowClass : ''
38023                         };
38024                         this.grid.fireEvent('rowclass', this, rowcfg);
38025                         alt.push(rowcfg.rowClass);
38026                     }
38027                     
38028                     rp.alt = alt.join(" ");
38029                     rp.cells = lcb.join("");
38030                     lbuf[lbuf.length] = rt.apply(rp);
38031                     rp.cells = cb.join("");
38032                     buf[buf.length] =  rt.apply(rp);
38033                 }
38034                 return [lbuf.join(""), buf.join("")];
38035             },
38036
38037     renderBody : function(){
38038         var markup = this.renderRows();
38039         var bt = this.templates.body;
38040         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38041     },
38042
38043     /**
38044      * Refreshes the grid
38045      * @param {Boolean} headersToo
38046      */
38047     refresh : function(headersToo){
38048         this.fireEvent("beforerefresh", this);
38049         this.grid.stopEditing();
38050         var result = this.renderBody();
38051         this.lockedBody.update(result[0]);
38052         this.mainBody.update(result[1]);
38053         if(headersToo === true){
38054             this.updateHeaders();
38055             this.updateColumns();
38056             this.updateSplitters();
38057             this.updateHeaderSortState();
38058         }
38059         this.syncRowHeights();
38060         this.layout();
38061         this.fireEvent("refresh", this);
38062     },
38063
38064     handleColumnMove : function(cm, oldIndex, newIndex){
38065         this.indexMap = null;
38066         var s = this.getScrollState();
38067         this.refresh(true);
38068         this.restoreScroll(s);
38069         this.afterMove(newIndex);
38070     },
38071
38072     afterMove : function(colIndex){
38073         if(this.enableMoveAnim && Roo.enableFx){
38074             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38075         }
38076         // if multisort - fix sortOrder, and reload..
38077         if (this.grid.dataSource.multiSort) {
38078             // the we can call sort again..
38079             var dm = this.grid.dataSource;
38080             var cm = this.grid.colModel;
38081             var so = [];
38082             for(var i = 0; i < cm.config.length; i++ ) {
38083                 
38084                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38085                     continue; // dont' bother, it's not in sort list or being set.
38086                 }
38087                 
38088                 so.push(cm.config[i].dataIndex);
38089             };
38090             dm.sortOrder = so;
38091             dm.load(dm.lastOptions);
38092             
38093             
38094         }
38095         
38096     },
38097
38098     updateCell : function(dm, rowIndex, dataIndex){
38099         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38100         if(typeof colIndex == "undefined"){ // not present in grid
38101             return;
38102         }
38103         var cm = this.grid.colModel;
38104         var cell = this.getCell(rowIndex, colIndex);
38105         var cellText = this.getCellText(rowIndex, colIndex);
38106
38107         var p = {
38108             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38109             id : cm.getColumnId(colIndex),
38110             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38111         };
38112         var renderer = cm.getRenderer(colIndex);
38113         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38114         if(typeof val == "undefined" || val === "") {
38115             val = "&#160;";
38116         }
38117         cellText.innerHTML = val;
38118         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38119         this.syncRowHeights(rowIndex, rowIndex);
38120     },
38121
38122     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38123         var maxWidth = 0;
38124         if(this.grid.autoSizeHeaders){
38125             var h = this.getHeaderCellMeasure(colIndex);
38126             maxWidth = Math.max(maxWidth, h.scrollWidth);
38127         }
38128         var tb, index;
38129         if(this.cm.isLocked(colIndex)){
38130             tb = this.getLockedTable();
38131             index = colIndex;
38132         }else{
38133             tb = this.getBodyTable();
38134             index = colIndex - this.cm.getLockedCount();
38135         }
38136         if(tb && tb.rows){
38137             var rows = tb.rows;
38138             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38139             for(var i = 0; i < stopIndex; i++){
38140                 var cell = rows[i].childNodes[index].firstChild;
38141                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38142             }
38143         }
38144         return maxWidth + /*margin for error in IE*/ 5;
38145     },
38146     /**
38147      * Autofit a column to its content.
38148      * @param {Number} colIndex
38149      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38150      */
38151      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38152          if(this.cm.isHidden(colIndex)){
38153              return; // can't calc a hidden column
38154          }
38155         if(forceMinSize){
38156             var cid = this.cm.getColumnId(colIndex);
38157             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38158            if(this.grid.autoSizeHeaders){
38159                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38160            }
38161         }
38162         var newWidth = this.calcColumnWidth(colIndex);
38163         this.cm.setColumnWidth(colIndex,
38164             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38165         if(!suppressEvent){
38166             this.grid.fireEvent("columnresize", colIndex, newWidth);
38167         }
38168     },
38169
38170     /**
38171      * Autofits all columns to their content and then expands to fit any extra space in the grid
38172      */
38173      autoSizeColumns : function(){
38174         var cm = this.grid.colModel;
38175         var colCount = cm.getColumnCount();
38176         for(var i = 0; i < colCount; i++){
38177             this.autoSizeColumn(i, true, true);
38178         }
38179         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38180             this.fitColumns();
38181         }else{
38182             this.updateColumns();
38183             this.layout();
38184         }
38185     },
38186
38187     /**
38188      * Autofits all columns to the grid's width proportionate with their current size
38189      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38190      */
38191     fitColumns : function(reserveScrollSpace){
38192         var cm = this.grid.colModel;
38193         var colCount = cm.getColumnCount();
38194         var cols = [];
38195         var width = 0;
38196         var i, w;
38197         for (i = 0; i < colCount; i++){
38198             if(!cm.isHidden(i) && !cm.isFixed(i)){
38199                 w = cm.getColumnWidth(i);
38200                 cols.push(i);
38201                 cols.push(w);
38202                 width += w;
38203             }
38204         }
38205         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38206         if(reserveScrollSpace){
38207             avail -= 17;
38208         }
38209         var frac = (avail - cm.getTotalWidth())/width;
38210         while (cols.length){
38211             w = cols.pop();
38212             i = cols.pop();
38213             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38214         }
38215         this.updateColumns();
38216         this.layout();
38217     },
38218
38219     onRowSelect : function(rowIndex){
38220         var row = this.getRowComposite(rowIndex);
38221         row.addClass("x-grid-row-selected");
38222     },
38223
38224     onRowDeselect : function(rowIndex){
38225         var row = this.getRowComposite(rowIndex);
38226         row.removeClass("x-grid-row-selected");
38227     },
38228
38229     onCellSelect : function(row, col){
38230         var cell = this.getCell(row, col);
38231         if(cell){
38232             Roo.fly(cell).addClass("x-grid-cell-selected");
38233         }
38234     },
38235
38236     onCellDeselect : function(row, col){
38237         var cell = this.getCell(row, col);
38238         if(cell){
38239             Roo.fly(cell).removeClass("x-grid-cell-selected");
38240         }
38241     },
38242
38243     updateHeaderSortState : function(){
38244         
38245         // sort state can be single { field: xxx, direction : yyy}
38246         // or   { xxx=>ASC , yyy : DESC ..... }
38247         
38248         var mstate = {};
38249         if (!this.ds.multiSort) { 
38250             var state = this.ds.getSortState();
38251             if(!state){
38252                 return;
38253             }
38254             mstate[state.field] = state.direction;
38255             // FIXME... - this is not used here.. but might be elsewhere..
38256             this.sortState = state;
38257             
38258         } else {
38259             mstate = this.ds.sortToggle;
38260         }
38261         //remove existing sort classes..
38262         
38263         var sc = this.sortClasses;
38264         var hds = this.el.select(this.headerSelector).removeClass(sc);
38265         
38266         for(var f in mstate) {
38267         
38268             var sortColumn = this.cm.findColumnIndex(f);
38269             
38270             if(sortColumn != -1){
38271                 var sortDir = mstate[f];        
38272                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38273             }
38274         }
38275         
38276          
38277         
38278     },
38279
38280
38281     handleHeaderClick : function(g, index,e){
38282         
38283         Roo.log("header click");
38284         
38285         if (Roo.isTouch) {
38286             // touch events on header are handled by context
38287             this.handleHdCtx(g,index,e);
38288             return;
38289         }
38290         
38291         
38292         if(this.headersDisabled){
38293             return;
38294         }
38295         var dm = g.dataSource, cm = g.colModel;
38296         if(!cm.isSortable(index)){
38297             return;
38298         }
38299         g.stopEditing();
38300         
38301         if (dm.multiSort) {
38302             // update the sortOrder
38303             var so = [];
38304             for(var i = 0; i < cm.config.length; i++ ) {
38305                 
38306                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38307                     continue; // dont' bother, it's not in sort list or being set.
38308                 }
38309                 
38310                 so.push(cm.config[i].dataIndex);
38311             };
38312             dm.sortOrder = so;
38313         }
38314         
38315         
38316         dm.sort(cm.getDataIndex(index));
38317     },
38318
38319
38320     destroy : function(){
38321         if(this.colMenu){
38322             this.colMenu.removeAll();
38323             Roo.menu.MenuMgr.unregister(this.colMenu);
38324             this.colMenu.getEl().remove();
38325             delete this.colMenu;
38326         }
38327         if(this.hmenu){
38328             this.hmenu.removeAll();
38329             Roo.menu.MenuMgr.unregister(this.hmenu);
38330             this.hmenu.getEl().remove();
38331             delete this.hmenu;
38332         }
38333         if(this.grid.enableColumnMove){
38334             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38335             if(dds){
38336                 for(var dd in dds){
38337                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38338                         var elid = dds[dd].dragElId;
38339                         dds[dd].unreg();
38340                         Roo.get(elid).remove();
38341                     } else if(dds[dd].config.isTarget){
38342                         dds[dd].proxyTop.remove();
38343                         dds[dd].proxyBottom.remove();
38344                         dds[dd].unreg();
38345                     }
38346                     if(Roo.dd.DDM.locationCache[dd]){
38347                         delete Roo.dd.DDM.locationCache[dd];
38348                     }
38349                 }
38350                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38351             }
38352         }
38353         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38354         this.bind(null, null);
38355         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38356     },
38357
38358     handleLockChange : function(){
38359         this.refresh(true);
38360     },
38361
38362     onDenyColumnLock : function(){
38363
38364     },
38365
38366     onDenyColumnHide : function(){
38367
38368     },
38369
38370     handleHdMenuClick : function(item){
38371         var index = this.hdCtxIndex;
38372         var cm = this.cm, ds = this.ds;
38373         switch(item.id){
38374             case "asc":
38375                 ds.sort(cm.getDataIndex(index), "ASC");
38376                 break;
38377             case "desc":
38378                 ds.sort(cm.getDataIndex(index), "DESC");
38379                 break;
38380             case "lock":
38381                 var lc = cm.getLockedCount();
38382                 if(cm.getColumnCount(true) <= lc+1){
38383                     this.onDenyColumnLock();
38384                     return;
38385                 }
38386                 if(lc != index){
38387                     cm.setLocked(index, true, true);
38388                     cm.moveColumn(index, lc);
38389                     this.grid.fireEvent("columnmove", index, lc);
38390                 }else{
38391                     cm.setLocked(index, true);
38392                 }
38393             break;
38394             case "unlock":
38395                 var lc = cm.getLockedCount();
38396                 if((lc-1) != index){
38397                     cm.setLocked(index, false, true);
38398                     cm.moveColumn(index, lc-1);
38399                     this.grid.fireEvent("columnmove", index, lc-1);
38400                 }else{
38401                     cm.setLocked(index, false);
38402                 }
38403             break;
38404             case 'wider': // used to expand cols on touch..
38405             case 'narrow':
38406                 var cw = cm.getColumnWidth(index);
38407                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38408                 cw = Math.max(0, cw);
38409                 cw = Math.min(cw,4000);
38410                 cm.setColumnWidth(index, cw);
38411                 break;
38412                 
38413             default:
38414                 index = cm.getIndexById(item.id.substr(4));
38415                 if(index != -1){
38416                     if(item.checked && cm.getColumnCount(true) <= 1){
38417                         this.onDenyColumnHide();
38418                         return false;
38419                     }
38420                     cm.setHidden(index, item.checked);
38421                 }
38422         }
38423         return true;
38424     },
38425
38426     beforeColMenuShow : function(){
38427         var cm = this.cm,  colCount = cm.getColumnCount();
38428         this.colMenu.removeAll();
38429         for(var i = 0; i < colCount; i++){
38430             this.colMenu.add(new Roo.menu.CheckItem({
38431                 id: "col-"+cm.getColumnId(i),
38432                 text: cm.getColumnHeader(i),
38433                 checked: !cm.isHidden(i),
38434                 hideOnClick:false
38435             }));
38436         }
38437     },
38438
38439     handleHdCtx : function(g, index, e){
38440         e.stopEvent();
38441         var hd = this.getHeaderCell(index);
38442         this.hdCtxIndex = index;
38443         var ms = this.hmenu.items, cm = this.cm;
38444         ms.get("asc").setDisabled(!cm.isSortable(index));
38445         ms.get("desc").setDisabled(!cm.isSortable(index));
38446         if(this.grid.enableColLock !== false){
38447             ms.get("lock").setDisabled(cm.isLocked(index));
38448             ms.get("unlock").setDisabled(!cm.isLocked(index));
38449         }
38450         this.hmenu.show(hd, "tl-bl");
38451     },
38452
38453     handleHdOver : function(e){
38454         var hd = this.findHeaderCell(e.getTarget());
38455         if(hd && !this.headersDisabled){
38456             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38457                this.fly(hd).addClass("x-grid-hd-over");
38458             }
38459         }
38460     },
38461
38462     handleHdOut : function(e){
38463         var hd = this.findHeaderCell(e.getTarget());
38464         if(hd){
38465             this.fly(hd).removeClass("x-grid-hd-over");
38466         }
38467     },
38468
38469     handleSplitDblClick : function(e, t){
38470         var i = this.getCellIndex(t);
38471         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38472             this.autoSizeColumn(i, true);
38473             this.layout();
38474         }
38475     },
38476
38477     render : function(){
38478
38479         var cm = this.cm;
38480         var colCount = cm.getColumnCount();
38481
38482         if(this.grid.monitorWindowResize === true){
38483             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38484         }
38485         var header = this.renderHeaders();
38486         var body = this.templates.body.apply({rows:""});
38487         var html = this.templates.master.apply({
38488             lockedBody: body,
38489             body: body,
38490             lockedHeader: header[0],
38491             header: header[1]
38492         });
38493
38494         //this.updateColumns();
38495
38496         this.grid.getGridEl().dom.innerHTML = html;
38497
38498         this.initElements();
38499         
38500         // a kludge to fix the random scolling effect in webkit
38501         this.el.on("scroll", function() {
38502             this.el.dom.scrollTop=0; // hopefully not recursive..
38503         },this);
38504
38505         this.scroller.on("scroll", this.handleScroll, this);
38506         this.lockedBody.on("mousewheel", this.handleWheel, this);
38507         this.mainBody.on("mousewheel", this.handleWheel, this);
38508
38509         this.mainHd.on("mouseover", this.handleHdOver, this);
38510         this.mainHd.on("mouseout", this.handleHdOut, this);
38511         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38512                 {delegate: "."+this.splitClass});
38513
38514         this.lockedHd.on("mouseover", this.handleHdOver, this);
38515         this.lockedHd.on("mouseout", this.handleHdOut, this);
38516         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38517                 {delegate: "."+this.splitClass});
38518
38519         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38520             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38521         }
38522
38523         this.updateSplitters();
38524
38525         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38526             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38527             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38528         }
38529
38530         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38531             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38532             this.hmenu.add(
38533                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38534                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38535             );
38536             if(this.grid.enableColLock !== false){
38537                 this.hmenu.add('-',
38538                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38539                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38540                 );
38541             }
38542             if (Roo.isTouch) {
38543                  this.hmenu.add('-',
38544                     {id:"wider", text: this.columnsWiderText},
38545                     {id:"narrow", text: this.columnsNarrowText }
38546                 );
38547                 
38548                  
38549             }
38550             
38551             if(this.grid.enableColumnHide !== false){
38552
38553                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38554                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38555                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38556
38557                 this.hmenu.add('-',
38558                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38559                 );
38560             }
38561             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38562
38563             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38564         }
38565
38566         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38567             this.dd = new Roo.grid.GridDragZone(this.grid, {
38568                 ddGroup : this.grid.ddGroup || 'GridDD'
38569             });
38570             
38571         }
38572
38573         /*
38574         for(var i = 0; i < colCount; i++){
38575             if(cm.isHidden(i)){
38576                 this.hideColumn(i);
38577             }
38578             if(cm.config[i].align){
38579                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38580                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38581             }
38582         }*/
38583         
38584         this.updateHeaderSortState();
38585
38586         this.beforeInitialResize();
38587         this.layout(true);
38588
38589         // two part rendering gives faster view to the user
38590         this.renderPhase2.defer(1, this);
38591     },
38592
38593     renderPhase2 : function(){
38594         // render the rows now
38595         this.refresh();
38596         if(this.grid.autoSizeColumns){
38597             this.autoSizeColumns();
38598         }
38599     },
38600
38601     beforeInitialResize : function(){
38602
38603     },
38604
38605     onColumnSplitterMoved : function(i, w){
38606         this.userResized = true;
38607         var cm = this.grid.colModel;
38608         cm.setColumnWidth(i, w, true);
38609         var cid = cm.getColumnId(i);
38610         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38611         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38612         this.updateSplitters();
38613         this.layout();
38614         this.grid.fireEvent("columnresize", i, w);
38615     },
38616
38617     syncRowHeights : function(startIndex, endIndex){
38618         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38619             startIndex = startIndex || 0;
38620             var mrows = this.getBodyTable().rows;
38621             var lrows = this.getLockedTable().rows;
38622             var len = mrows.length-1;
38623             endIndex = Math.min(endIndex || len, len);
38624             for(var i = startIndex; i <= endIndex; i++){
38625                 var m = mrows[i], l = lrows[i];
38626                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38627                 m.style.height = l.style.height = h + "px";
38628             }
38629         }
38630     },
38631
38632     layout : function(initialRender, is2ndPass){
38633         var g = this.grid;
38634         var auto = g.autoHeight;
38635         var scrollOffset = 16;
38636         var c = g.getGridEl(), cm = this.cm,
38637                 expandCol = g.autoExpandColumn,
38638                 gv = this;
38639         //c.beginMeasure();
38640
38641         if(!c.dom.offsetWidth){ // display:none?
38642             if(initialRender){
38643                 this.lockedWrap.show();
38644                 this.mainWrap.show();
38645             }
38646             return;
38647         }
38648
38649         var hasLock = this.cm.isLocked(0);
38650
38651         var tbh = this.headerPanel.getHeight();
38652         var bbh = this.footerPanel.getHeight();
38653
38654         if(auto){
38655             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38656             var newHeight = ch + c.getBorderWidth("tb");
38657             if(g.maxHeight){
38658                 newHeight = Math.min(g.maxHeight, newHeight);
38659             }
38660             c.setHeight(newHeight);
38661         }
38662
38663         if(g.autoWidth){
38664             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38665         }
38666
38667         var s = this.scroller;
38668
38669         var csize = c.getSize(true);
38670
38671         this.el.setSize(csize.width, csize.height);
38672
38673         this.headerPanel.setWidth(csize.width);
38674         this.footerPanel.setWidth(csize.width);
38675
38676         var hdHeight = this.mainHd.getHeight();
38677         var vw = csize.width;
38678         var vh = csize.height - (tbh + bbh);
38679
38680         s.setSize(vw, vh);
38681
38682         var bt = this.getBodyTable();
38683         
38684         if(cm.getLockedCount() == cm.config.length){
38685             bt = this.getLockedTable();
38686         }
38687         
38688         var ltWidth = hasLock ?
38689                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38690
38691         var scrollHeight = bt.offsetHeight;
38692         var scrollWidth = ltWidth + bt.offsetWidth;
38693         var vscroll = false, hscroll = false;
38694
38695         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38696
38697         var lw = this.lockedWrap, mw = this.mainWrap;
38698         var lb = this.lockedBody, mb = this.mainBody;
38699
38700         setTimeout(function(){
38701             var t = s.dom.offsetTop;
38702             var w = s.dom.clientWidth,
38703                 h = s.dom.clientHeight;
38704
38705             lw.setTop(t);
38706             lw.setSize(ltWidth, h);
38707
38708             mw.setLeftTop(ltWidth, t);
38709             mw.setSize(w-ltWidth, h);
38710
38711             lb.setHeight(h-hdHeight);
38712             mb.setHeight(h-hdHeight);
38713
38714             if(is2ndPass !== true && !gv.userResized && expandCol){
38715                 // high speed resize without full column calculation
38716                 
38717                 var ci = cm.getIndexById(expandCol);
38718                 if (ci < 0) {
38719                     ci = cm.findColumnIndex(expandCol);
38720                 }
38721                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38722                 var expandId = cm.getColumnId(ci);
38723                 var  tw = cm.getTotalWidth(false);
38724                 var currentWidth = cm.getColumnWidth(ci);
38725                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38726                 if(currentWidth != cw){
38727                     cm.setColumnWidth(ci, cw, true);
38728                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38729                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38730                     gv.updateSplitters();
38731                     gv.layout(false, true);
38732                 }
38733             }
38734
38735             if(initialRender){
38736                 lw.show();
38737                 mw.show();
38738             }
38739             //c.endMeasure();
38740         }, 10);
38741     },
38742
38743     onWindowResize : function(){
38744         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38745             return;
38746         }
38747         this.layout();
38748     },
38749
38750     appendFooter : function(parentEl){
38751         return null;
38752     },
38753
38754     sortAscText : "Sort Ascending",
38755     sortDescText : "Sort Descending",
38756     lockText : "Lock Column",
38757     unlockText : "Unlock Column",
38758     columnsText : "Columns",
38759  
38760     columnsWiderText : "Wider",
38761     columnsNarrowText : "Thinner"
38762 });
38763
38764
38765 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38766     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38767     this.proxy.el.addClass('x-grid3-col-dd');
38768 };
38769
38770 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38771     handleMouseDown : function(e){
38772
38773     },
38774
38775     callHandleMouseDown : function(e){
38776         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38777     }
38778 });
38779 /*
38780  * Based on:
38781  * Ext JS Library 1.1.1
38782  * Copyright(c) 2006-2007, Ext JS, LLC.
38783  *
38784  * Originally Released Under LGPL - original licence link has changed is not relivant.
38785  *
38786  * Fork - LGPL
38787  * <script type="text/javascript">
38788  */
38789  
38790 // private
38791 // This is a support class used internally by the Grid components
38792 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38793     this.grid = grid;
38794     this.view = grid.getView();
38795     this.proxy = this.view.resizeProxy;
38796     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38797         "gridSplitters" + this.grid.getGridEl().id, {
38798         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38799     });
38800     this.setHandleElId(Roo.id(hd));
38801     this.setOuterHandleElId(Roo.id(hd2));
38802     this.scroll = false;
38803 };
38804 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38805     fly: Roo.Element.fly,
38806
38807     b4StartDrag : function(x, y){
38808         this.view.headersDisabled = true;
38809         this.proxy.setHeight(this.view.mainWrap.getHeight());
38810         var w = this.cm.getColumnWidth(this.cellIndex);
38811         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38812         this.resetConstraints();
38813         this.setXConstraint(minw, 1000);
38814         this.setYConstraint(0, 0);
38815         this.minX = x - minw;
38816         this.maxX = x + 1000;
38817         this.startPos = x;
38818         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38819     },
38820
38821
38822     handleMouseDown : function(e){
38823         ev = Roo.EventObject.setEvent(e);
38824         var t = this.fly(ev.getTarget());
38825         if(t.hasClass("x-grid-split")){
38826             this.cellIndex = this.view.getCellIndex(t.dom);
38827             this.split = t.dom;
38828             this.cm = this.grid.colModel;
38829             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38830                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38831             }
38832         }
38833     },
38834
38835     endDrag : function(e){
38836         this.view.headersDisabled = false;
38837         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38838         var diff = endX - this.startPos;
38839         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38840     },
38841
38842     autoOffset : function(){
38843         this.setDelta(0,0);
38844     }
38845 });/*
38846  * Based on:
38847  * Ext JS Library 1.1.1
38848  * Copyright(c) 2006-2007, Ext JS, LLC.
38849  *
38850  * Originally Released Under LGPL - original licence link has changed is not relivant.
38851  *
38852  * Fork - LGPL
38853  * <script type="text/javascript">
38854  */
38855  
38856 // private
38857 // This is a support class used internally by the Grid components
38858 Roo.grid.GridDragZone = function(grid, config){
38859     this.view = grid.getView();
38860     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38861     if(this.view.lockedBody){
38862         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38863         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38864     }
38865     this.scroll = false;
38866     this.grid = grid;
38867     this.ddel = document.createElement('div');
38868     this.ddel.className = 'x-grid-dd-wrap';
38869 };
38870
38871 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38872     ddGroup : "GridDD",
38873
38874     getDragData : function(e){
38875         var t = Roo.lib.Event.getTarget(e);
38876         var rowIndex = this.view.findRowIndex(t);
38877         var sm = this.grid.selModel;
38878             
38879         //Roo.log(rowIndex);
38880         
38881         if (sm.getSelectedCell) {
38882             // cell selection..
38883             if (!sm.getSelectedCell()) {
38884                 return false;
38885             }
38886             if (rowIndex != sm.getSelectedCell()[0]) {
38887                 return false;
38888             }
38889         
38890         }
38891         
38892         if(rowIndex !== false){
38893             
38894             // if editorgrid.. 
38895             
38896             
38897             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38898                
38899             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38900               //  
38901             //}
38902             if (e.hasModifier()){
38903                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38904             }
38905             
38906             Roo.log("getDragData");
38907             
38908             return {
38909                 grid: this.grid,
38910                 ddel: this.ddel,
38911                 rowIndex: rowIndex,
38912                 selections:sm.getSelections ? sm.getSelections() : (
38913                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38914                 )
38915             };
38916         }
38917         return false;
38918     },
38919
38920     onInitDrag : function(e){
38921         var data = this.dragData;
38922         this.ddel.innerHTML = this.grid.getDragDropText();
38923         this.proxy.update(this.ddel);
38924         // fire start drag?
38925     },
38926
38927     afterRepair : function(){
38928         this.dragging = false;
38929     },
38930
38931     getRepairXY : function(e, data){
38932         return false;
38933     },
38934
38935     onEndDrag : function(data, e){
38936         // fire end drag?
38937     },
38938
38939     onValidDrop : function(dd, e, id){
38940         // fire drag drop?
38941         this.hideProxy();
38942     },
38943
38944     beforeInvalidDrop : function(e, id){
38945
38946     }
38947 });/*
38948  * Based on:
38949  * Ext JS Library 1.1.1
38950  * Copyright(c) 2006-2007, Ext JS, LLC.
38951  *
38952  * Originally Released Under LGPL - original licence link has changed is not relivant.
38953  *
38954  * Fork - LGPL
38955  * <script type="text/javascript">
38956  */
38957  
38958
38959 /**
38960  * @class Roo.grid.ColumnModel
38961  * @extends Roo.util.Observable
38962  * This is the default implementation of a ColumnModel used by the Grid. It defines
38963  * the columns in the grid.
38964  * <br>Usage:<br>
38965  <pre><code>
38966  var colModel = new Roo.grid.ColumnModel([
38967         {header: "Ticker", width: 60, sortable: true, locked: true},
38968         {header: "Company Name", width: 150, sortable: true},
38969         {header: "Market Cap.", width: 100, sortable: true},
38970         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38971         {header: "Employees", width: 100, sortable: true, resizable: false}
38972  ]);
38973  </code></pre>
38974  * <p>
38975  
38976  * The config options listed for this class are options which may appear in each
38977  * individual column definition.
38978  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38979  * @constructor
38980  * @param {Object} config An Array of column config objects. See this class's
38981  * config objects for details.
38982 */
38983 Roo.grid.ColumnModel = function(config){
38984         /**
38985      * The config passed into the constructor
38986      */
38987     this.config = config;
38988     this.lookup = {};
38989
38990     // if no id, create one
38991     // if the column does not have a dataIndex mapping,
38992     // map it to the order it is in the config
38993     for(var i = 0, len = config.length; i < len; i++){
38994         var c = config[i];
38995         if(typeof c.dataIndex == "undefined"){
38996             c.dataIndex = i;
38997         }
38998         if(typeof c.renderer == "string"){
38999             c.renderer = Roo.util.Format[c.renderer];
39000         }
39001         if(typeof c.id == "undefined"){
39002             c.id = Roo.id();
39003         }
39004         if(c.editor && c.editor.xtype){
39005             c.editor  = Roo.factory(c.editor, Roo.grid);
39006         }
39007         if(c.editor && c.editor.isFormField){
39008             c.editor = new Roo.grid.GridEditor(c.editor);
39009         }
39010         this.lookup[c.id] = c;
39011     }
39012
39013     /**
39014      * The width of columns which have no width specified (defaults to 100)
39015      * @type Number
39016      */
39017     this.defaultWidth = 100;
39018
39019     /**
39020      * Default sortable of columns which have no sortable specified (defaults to false)
39021      * @type Boolean
39022      */
39023     this.defaultSortable = false;
39024
39025     this.addEvents({
39026         /**
39027              * @event widthchange
39028              * Fires when the width of a column changes.
39029              * @param {ColumnModel} this
39030              * @param {Number} columnIndex The column index
39031              * @param {Number} newWidth The new width
39032              */
39033             "widthchange": true,
39034         /**
39035              * @event headerchange
39036              * Fires when the text of a header changes.
39037              * @param {ColumnModel} this
39038              * @param {Number} columnIndex The column index
39039              * @param {Number} newText The new header text
39040              */
39041             "headerchange": true,
39042         /**
39043              * @event hiddenchange
39044              * Fires when a column is hidden or "unhidden".
39045              * @param {ColumnModel} this
39046              * @param {Number} columnIndex The column index
39047              * @param {Boolean} hidden true if hidden, false otherwise
39048              */
39049             "hiddenchange": true,
39050             /**
39051          * @event columnmoved
39052          * Fires when a column is moved.
39053          * @param {ColumnModel} this
39054          * @param {Number} oldIndex
39055          * @param {Number} newIndex
39056          */
39057         "columnmoved" : true,
39058         /**
39059          * @event columlockchange
39060          * Fires when a column's locked state is changed
39061          * @param {ColumnModel} this
39062          * @param {Number} colIndex
39063          * @param {Boolean} locked true if locked
39064          */
39065         "columnlockchange" : true
39066     });
39067     Roo.grid.ColumnModel.superclass.constructor.call(this);
39068 };
39069 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39070     /**
39071      * @cfg {String} header The header text to display in the Grid view.
39072      */
39073     /**
39074      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39075      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39076      * specified, the column's index is used as an index into the Record's data Array.
39077      */
39078     /**
39079      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39080      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39081      */
39082     /**
39083      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39084      * Defaults to the value of the {@link #defaultSortable} property.
39085      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39086      */
39087     /**
39088      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39089      */
39090     /**
39091      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39092      */
39093     /**
39094      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39095      */
39096     /**
39097      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39098      */
39099     /**
39100      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39101      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39102      * default renderer uses the raw data value. If an object is returned (bootstrap only)
39103      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39104      */
39105        /**
39106      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39107      */
39108     /**
39109      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39110      */
39111     /**
39112      * @cfg {String} cursor (Optional)
39113      */
39114     /**
39115      * @cfg {String} tooltip (Optional)
39116      */
39117     /**
39118      * @cfg {Number} xs (Optional)
39119      */
39120     /**
39121      * @cfg {Number} sm (Optional)
39122      */
39123     /**
39124      * @cfg {Number} md (Optional)
39125      */
39126     /**
39127      * @cfg {Number} lg (Optional)
39128      */
39129     /**
39130      * Returns the id of the column at the specified index.
39131      * @param {Number} index The column index
39132      * @return {String} the id
39133      */
39134     getColumnId : function(index){
39135         return this.config[index].id;
39136     },
39137
39138     /**
39139      * Returns the column for a specified id.
39140      * @param {String} id The column id
39141      * @return {Object} the column
39142      */
39143     getColumnById : function(id){
39144         return this.lookup[id];
39145     },
39146
39147     
39148     /**
39149      * Returns the column for a specified dataIndex.
39150      * @param {String} dataIndex The column dataIndex
39151      * @return {Object|Boolean} the column or false if not found
39152      */
39153     getColumnByDataIndex: function(dataIndex){
39154         var index = this.findColumnIndex(dataIndex);
39155         return index > -1 ? this.config[index] : false;
39156     },
39157     
39158     /**
39159      * Returns the index for a specified column id.
39160      * @param {String} id The column id
39161      * @return {Number} the index, or -1 if not found
39162      */
39163     getIndexById : function(id){
39164         for(var i = 0, len = this.config.length; i < len; i++){
39165             if(this.config[i].id == id){
39166                 return i;
39167             }
39168         }
39169         return -1;
39170     },
39171     
39172     /**
39173      * Returns the index for a specified column dataIndex.
39174      * @param {String} dataIndex The column dataIndex
39175      * @return {Number} the index, or -1 if not found
39176      */
39177     
39178     findColumnIndex : function(dataIndex){
39179         for(var i = 0, len = this.config.length; i < len; i++){
39180             if(this.config[i].dataIndex == dataIndex){
39181                 return i;
39182             }
39183         }
39184         return -1;
39185     },
39186     
39187     
39188     moveColumn : function(oldIndex, newIndex){
39189         var c = this.config[oldIndex];
39190         this.config.splice(oldIndex, 1);
39191         this.config.splice(newIndex, 0, c);
39192         this.dataMap = null;
39193         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39194     },
39195
39196     isLocked : function(colIndex){
39197         return this.config[colIndex].locked === true;
39198     },
39199
39200     setLocked : function(colIndex, value, suppressEvent){
39201         if(this.isLocked(colIndex) == value){
39202             return;
39203         }
39204         this.config[colIndex].locked = value;
39205         if(!suppressEvent){
39206             this.fireEvent("columnlockchange", this, colIndex, value);
39207         }
39208     },
39209
39210     getTotalLockedWidth : function(){
39211         var totalWidth = 0;
39212         for(var i = 0; i < this.config.length; i++){
39213             if(this.isLocked(i) && !this.isHidden(i)){
39214                 this.totalWidth += this.getColumnWidth(i);
39215             }
39216         }
39217         return totalWidth;
39218     },
39219
39220     getLockedCount : function(){
39221         for(var i = 0, len = this.config.length; i < len; i++){
39222             if(!this.isLocked(i)){
39223                 return i;
39224             }
39225         }
39226         
39227         return this.config.length;
39228     },
39229
39230     /**
39231      * Returns the number of columns.
39232      * @return {Number}
39233      */
39234     getColumnCount : function(visibleOnly){
39235         if(visibleOnly === true){
39236             var c = 0;
39237             for(var i = 0, len = this.config.length; i < len; i++){
39238                 if(!this.isHidden(i)){
39239                     c++;
39240                 }
39241             }
39242             return c;
39243         }
39244         return this.config.length;
39245     },
39246
39247     /**
39248      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39249      * @param {Function} fn
39250      * @param {Object} scope (optional)
39251      * @return {Array} result
39252      */
39253     getColumnsBy : function(fn, scope){
39254         var r = [];
39255         for(var i = 0, len = this.config.length; i < len; i++){
39256             var c = this.config[i];
39257             if(fn.call(scope||this, c, i) === true){
39258                 r[r.length] = c;
39259             }
39260         }
39261         return r;
39262     },
39263
39264     /**
39265      * Returns true if the specified column is sortable.
39266      * @param {Number} col The column index
39267      * @return {Boolean}
39268      */
39269     isSortable : function(col){
39270         if(typeof this.config[col].sortable == "undefined"){
39271             return this.defaultSortable;
39272         }
39273         return this.config[col].sortable;
39274     },
39275
39276     /**
39277      * Returns the rendering (formatting) function defined for the column.
39278      * @param {Number} col The column index.
39279      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39280      */
39281     getRenderer : function(col){
39282         if(!this.config[col].renderer){
39283             return Roo.grid.ColumnModel.defaultRenderer;
39284         }
39285         return this.config[col].renderer;
39286     },
39287
39288     /**
39289      * Sets the rendering (formatting) function for a column.
39290      * @param {Number} col The column index
39291      * @param {Function} fn The function to use to process the cell's raw data
39292      * to return HTML markup for the grid view. The render function is called with
39293      * the following parameters:<ul>
39294      * <li>Data value.</li>
39295      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39296      * <li>css A CSS style string to apply to the table cell.</li>
39297      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39298      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39299      * <li>Row index</li>
39300      * <li>Column index</li>
39301      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39302      */
39303     setRenderer : function(col, fn){
39304         this.config[col].renderer = fn;
39305     },
39306
39307     /**
39308      * Returns the width for the specified column.
39309      * @param {Number} col The column index
39310      * @return {Number}
39311      */
39312     getColumnWidth : function(col){
39313         return this.config[col].width * 1 || this.defaultWidth;
39314     },
39315
39316     /**
39317      * Sets the width for a column.
39318      * @param {Number} col The column index
39319      * @param {Number} width The new width
39320      */
39321     setColumnWidth : function(col, width, suppressEvent){
39322         this.config[col].width = width;
39323         this.totalWidth = null;
39324         if(!suppressEvent){
39325              this.fireEvent("widthchange", this, col, width);
39326         }
39327     },
39328
39329     /**
39330      * Returns the total width of all columns.
39331      * @param {Boolean} includeHidden True to include hidden column widths
39332      * @return {Number}
39333      */
39334     getTotalWidth : function(includeHidden){
39335         if(!this.totalWidth){
39336             this.totalWidth = 0;
39337             for(var i = 0, len = this.config.length; i < len; i++){
39338                 if(includeHidden || !this.isHidden(i)){
39339                     this.totalWidth += this.getColumnWidth(i);
39340                 }
39341             }
39342         }
39343         return this.totalWidth;
39344     },
39345
39346     /**
39347      * Returns the header for the specified column.
39348      * @param {Number} col The column index
39349      * @return {String}
39350      */
39351     getColumnHeader : function(col){
39352         return this.config[col].header;
39353     },
39354
39355     /**
39356      * Sets the header for a column.
39357      * @param {Number} col The column index
39358      * @param {String} header The new header
39359      */
39360     setColumnHeader : function(col, header){
39361         this.config[col].header = header;
39362         this.fireEvent("headerchange", this, col, header);
39363     },
39364
39365     /**
39366      * Returns the tooltip for the specified column.
39367      * @param {Number} col The column index
39368      * @return {String}
39369      */
39370     getColumnTooltip : function(col){
39371             return this.config[col].tooltip;
39372     },
39373     /**
39374      * Sets the tooltip for a column.
39375      * @param {Number} col The column index
39376      * @param {String} tooltip The new tooltip
39377      */
39378     setColumnTooltip : function(col, tooltip){
39379             this.config[col].tooltip = tooltip;
39380     },
39381
39382     /**
39383      * Returns the dataIndex for the specified column.
39384      * @param {Number} col The column index
39385      * @return {Number}
39386      */
39387     getDataIndex : function(col){
39388         return this.config[col].dataIndex;
39389     },
39390
39391     /**
39392      * Sets the dataIndex for a column.
39393      * @param {Number} col The column index
39394      * @param {Number} dataIndex The new dataIndex
39395      */
39396     setDataIndex : function(col, dataIndex){
39397         this.config[col].dataIndex = dataIndex;
39398     },
39399
39400     
39401     
39402     /**
39403      * Returns true if the cell is editable.
39404      * @param {Number} colIndex The column index
39405      * @param {Number} rowIndex The row index - this is nto actually used..?
39406      * @return {Boolean}
39407      */
39408     isCellEditable : function(colIndex, rowIndex){
39409         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39410     },
39411
39412     /**
39413      * Returns the editor defined for the cell/column.
39414      * return false or null to disable editing.
39415      * @param {Number} colIndex The column index
39416      * @param {Number} rowIndex The row index
39417      * @return {Object}
39418      */
39419     getCellEditor : function(colIndex, rowIndex){
39420         return this.config[colIndex].editor;
39421     },
39422
39423     /**
39424      * Sets if a column is editable.
39425      * @param {Number} col The column index
39426      * @param {Boolean} editable True if the column is editable
39427      */
39428     setEditable : function(col, editable){
39429         this.config[col].editable = editable;
39430     },
39431
39432
39433     /**
39434      * Returns true if the column is hidden.
39435      * @param {Number} colIndex The column index
39436      * @return {Boolean}
39437      */
39438     isHidden : function(colIndex){
39439         return this.config[colIndex].hidden;
39440     },
39441
39442
39443     /**
39444      * Returns true if the column width cannot be changed
39445      */
39446     isFixed : function(colIndex){
39447         return this.config[colIndex].fixed;
39448     },
39449
39450     /**
39451      * Returns true if the column can be resized
39452      * @return {Boolean}
39453      */
39454     isResizable : function(colIndex){
39455         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39456     },
39457     /**
39458      * Sets if a column is hidden.
39459      * @param {Number} colIndex The column index
39460      * @param {Boolean} hidden True if the column is hidden
39461      */
39462     setHidden : function(colIndex, hidden){
39463         this.config[colIndex].hidden = hidden;
39464         this.totalWidth = null;
39465         this.fireEvent("hiddenchange", this, colIndex, hidden);
39466     },
39467
39468     /**
39469      * Sets the editor for a column.
39470      * @param {Number} col The column index
39471      * @param {Object} editor The editor object
39472      */
39473     setEditor : function(col, editor){
39474         this.config[col].editor = editor;
39475     }
39476 });
39477
39478 Roo.grid.ColumnModel.defaultRenderer = function(value){
39479         if(typeof value == "string" && value.length < 1){
39480             return "&#160;";
39481         }
39482         return value;
39483 };
39484
39485 // Alias for backwards compatibility
39486 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39487 /*
39488  * Based on:
39489  * Ext JS Library 1.1.1
39490  * Copyright(c) 2006-2007, Ext JS, LLC.
39491  *
39492  * Originally Released Under LGPL - original licence link has changed is not relivant.
39493  *
39494  * Fork - LGPL
39495  * <script type="text/javascript">
39496  */
39497
39498 /**
39499  * @class Roo.grid.AbstractSelectionModel
39500  * @extends Roo.util.Observable
39501  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39502  * implemented by descendant classes.  This class should not be directly instantiated.
39503  * @constructor
39504  */
39505 Roo.grid.AbstractSelectionModel = function(){
39506     this.locked = false;
39507     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39508 };
39509
39510 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39511     /** @ignore Called by the grid automatically. Do not call directly. */
39512     init : function(grid){
39513         this.grid = grid;
39514         this.initEvents();
39515     },
39516
39517     /**
39518      * Locks the selections.
39519      */
39520     lock : function(){
39521         this.locked = true;
39522     },
39523
39524     /**
39525      * Unlocks the selections.
39526      */
39527     unlock : function(){
39528         this.locked = false;
39529     },
39530
39531     /**
39532      * Returns true if the selections are locked.
39533      * @return {Boolean}
39534      */
39535     isLocked : function(){
39536         return this.locked;
39537     }
39538 });/*
39539  * Based on:
39540  * Ext JS Library 1.1.1
39541  * Copyright(c) 2006-2007, Ext JS, LLC.
39542  *
39543  * Originally Released Under LGPL - original licence link has changed is not relivant.
39544  *
39545  * Fork - LGPL
39546  * <script type="text/javascript">
39547  */
39548 /**
39549  * @extends Roo.grid.AbstractSelectionModel
39550  * @class Roo.grid.RowSelectionModel
39551  * The default SelectionModel used by {@link Roo.grid.Grid}.
39552  * It supports multiple selections and keyboard selection/navigation. 
39553  * @constructor
39554  * @param {Object} config
39555  */
39556 Roo.grid.RowSelectionModel = function(config){
39557     Roo.apply(this, config);
39558     this.selections = new Roo.util.MixedCollection(false, function(o){
39559         return o.id;
39560     });
39561
39562     this.last = false;
39563     this.lastActive = false;
39564
39565     this.addEvents({
39566         /**
39567              * @event selectionchange
39568              * Fires when the selection changes
39569              * @param {SelectionModel} this
39570              */
39571             "selectionchange" : true,
39572         /**
39573              * @event afterselectionchange
39574              * Fires after the selection changes (eg. by key press or clicking)
39575              * @param {SelectionModel} this
39576              */
39577             "afterselectionchange" : true,
39578         /**
39579              * @event beforerowselect
39580              * Fires when a row is selected being selected, return false to cancel.
39581              * @param {SelectionModel} this
39582              * @param {Number} rowIndex The selected index
39583              * @param {Boolean} keepExisting False if other selections will be cleared
39584              */
39585             "beforerowselect" : true,
39586         /**
39587              * @event rowselect
39588              * Fires when a row is selected.
39589              * @param {SelectionModel} this
39590              * @param {Number} rowIndex The selected index
39591              * @param {Roo.data.Record} r The record
39592              */
39593             "rowselect" : true,
39594         /**
39595              * @event rowdeselect
39596              * Fires when a row is deselected.
39597              * @param {SelectionModel} this
39598              * @param {Number} rowIndex The selected index
39599              */
39600         "rowdeselect" : true
39601     });
39602     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39603     this.locked = false;
39604 };
39605
39606 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39607     /**
39608      * @cfg {Boolean} singleSelect
39609      * True to allow selection of only one row at a time (defaults to false)
39610      */
39611     singleSelect : false,
39612
39613     // private
39614     initEvents : function(){
39615
39616         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39617             this.grid.on("mousedown", this.handleMouseDown, this);
39618         }else{ // allow click to work like normal
39619             this.grid.on("rowclick", this.handleDragableRowClick, this);
39620         }
39621
39622         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39623             "up" : function(e){
39624                 if(!e.shiftKey){
39625                     this.selectPrevious(e.shiftKey);
39626                 }else if(this.last !== false && this.lastActive !== false){
39627                     var last = this.last;
39628                     this.selectRange(this.last,  this.lastActive-1);
39629                     this.grid.getView().focusRow(this.lastActive);
39630                     if(last !== false){
39631                         this.last = last;
39632                     }
39633                 }else{
39634                     this.selectFirstRow();
39635                 }
39636                 this.fireEvent("afterselectionchange", this);
39637             },
39638             "down" : function(e){
39639                 if(!e.shiftKey){
39640                     this.selectNext(e.shiftKey);
39641                 }else if(this.last !== false && this.lastActive !== false){
39642                     var last = this.last;
39643                     this.selectRange(this.last,  this.lastActive+1);
39644                     this.grid.getView().focusRow(this.lastActive);
39645                     if(last !== false){
39646                         this.last = last;
39647                     }
39648                 }else{
39649                     this.selectFirstRow();
39650                 }
39651                 this.fireEvent("afterselectionchange", this);
39652             },
39653             scope: this
39654         });
39655
39656         var view = this.grid.view;
39657         view.on("refresh", this.onRefresh, this);
39658         view.on("rowupdated", this.onRowUpdated, this);
39659         view.on("rowremoved", this.onRemove, this);
39660     },
39661
39662     // private
39663     onRefresh : function(){
39664         var ds = this.grid.dataSource, i, v = this.grid.view;
39665         var s = this.selections;
39666         s.each(function(r){
39667             if((i = ds.indexOfId(r.id)) != -1){
39668                 v.onRowSelect(i);
39669                 s.add(ds.getAt(i)); // updating the selection relate data
39670             }else{
39671                 s.remove(r);
39672             }
39673         });
39674     },
39675
39676     // private
39677     onRemove : function(v, index, r){
39678         this.selections.remove(r);
39679     },
39680
39681     // private
39682     onRowUpdated : function(v, index, r){
39683         if(this.isSelected(r)){
39684             v.onRowSelect(index);
39685         }
39686     },
39687
39688     /**
39689      * Select records.
39690      * @param {Array} records The records to select
39691      * @param {Boolean} keepExisting (optional) True to keep existing selections
39692      */
39693     selectRecords : function(records, keepExisting){
39694         if(!keepExisting){
39695             this.clearSelections();
39696         }
39697         var ds = this.grid.dataSource;
39698         for(var i = 0, len = records.length; i < len; i++){
39699             this.selectRow(ds.indexOf(records[i]), true);
39700         }
39701     },
39702
39703     /**
39704      * Gets the number of selected rows.
39705      * @return {Number}
39706      */
39707     getCount : function(){
39708         return this.selections.length;
39709     },
39710
39711     /**
39712      * Selects the first row in the grid.
39713      */
39714     selectFirstRow : function(){
39715         this.selectRow(0);
39716     },
39717
39718     /**
39719      * Select the last row.
39720      * @param {Boolean} keepExisting (optional) True to keep existing selections
39721      */
39722     selectLastRow : function(keepExisting){
39723         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39724     },
39725
39726     /**
39727      * Selects the row immediately following the last selected row.
39728      * @param {Boolean} keepExisting (optional) True to keep existing selections
39729      */
39730     selectNext : function(keepExisting){
39731         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39732             this.selectRow(this.last+1, keepExisting);
39733             this.grid.getView().focusRow(this.last);
39734         }
39735     },
39736
39737     /**
39738      * Selects the row that precedes the last selected row.
39739      * @param {Boolean} keepExisting (optional) True to keep existing selections
39740      */
39741     selectPrevious : function(keepExisting){
39742         if(this.last){
39743             this.selectRow(this.last-1, keepExisting);
39744             this.grid.getView().focusRow(this.last);
39745         }
39746     },
39747
39748     /**
39749      * Returns the selected records
39750      * @return {Array} Array of selected records
39751      */
39752     getSelections : function(){
39753         return [].concat(this.selections.items);
39754     },
39755
39756     /**
39757      * Returns the first selected record.
39758      * @return {Record}
39759      */
39760     getSelected : function(){
39761         return this.selections.itemAt(0);
39762     },
39763
39764
39765     /**
39766      * Clears all selections.
39767      */
39768     clearSelections : function(fast){
39769         if(this.locked) {
39770             return;
39771         }
39772         if(fast !== true){
39773             var ds = this.grid.dataSource;
39774             var s = this.selections;
39775             s.each(function(r){
39776                 this.deselectRow(ds.indexOfId(r.id));
39777             }, this);
39778             s.clear();
39779         }else{
39780             this.selections.clear();
39781         }
39782         this.last = false;
39783     },
39784
39785
39786     /**
39787      * Selects all rows.
39788      */
39789     selectAll : function(){
39790         if(this.locked) {
39791             return;
39792         }
39793         this.selections.clear();
39794         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39795             this.selectRow(i, true);
39796         }
39797     },
39798
39799     /**
39800      * Returns True if there is a selection.
39801      * @return {Boolean}
39802      */
39803     hasSelection : function(){
39804         return this.selections.length > 0;
39805     },
39806
39807     /**
39808      * Returns True if the specified row is selected.
39809      * @param {Number/Record} record The record or index of the record to check
39810      * @return {Boolean}
39811      */
39812     isSelected : function(index){
39813         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39814         return (r && this.selections.key(r.id) ? true : false);
39815     },
39816
39817     /**
39818      * Returns True if the specified record id is selected.
39819      * @param {String} id The id of record to check
39820      * @return {Boolean}
39821      */
39822     isIdSelected : function(id){
39823         return (this.selections.key(id) ? true : false);
39824     },
39825
39826     // private
39827     handleMouseDown : function(e, t){
39828         var view = this.grid.getView(), rowIndex;
39829         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39830             return;
39831         };
39832         if(e.shiftKey && this.last !== false){
39833             var last = this.last;
39834             this.selectRange(last, rowIndex, e.ctrlKey);
39835             this.last = last; // reset the last
39836             view.focusRow(rowIndex);
39837         }else{
39838             var isSelected = this.isSelected(rowIndex);
39839             if(e.button !== 0 && isSelected){
39840                 view.focusRow(rowIndex);
39841             }else if(e.ctrlKey && isSelected){
39842                 this.deselectRow(rowIndex);
39843             }else if(!isSelected){
39844                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39845                 view.focusRow(rowIndex);
39846             }
39847         }
39848         this.fireEvent("afterselectionchange", this);
39849     },
39850     // private
39851     handleDragableRowClick :  function(grid, rowIndex, e) 
39852     {
39853         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39854             this.selectRow(rowIndex, false);
39855             grid.view.focusRow(rowIndex);
39856              this.fireEvent("afterselectionchange", this);
39857         }
39858     },
39859     
39860     /**
39861      * Selects multiple rows.
39862      * @param {Array} rows Array of the indexes of the row to select
39863      * @param {Boolean} keepExisting (optional) True to keep existing selections
39864      */
39865     selectRows : function(rows, keepExisting){
39866         if(!keepExisting){
39867             this.clearSelections();
39868         }
39869         for(var i = 0, len = rows.length; i < len; i++){
39870             this.selectRow(rows[i], true);
39871         }
39872     },
39873
39874     /**
39875      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39876      * @param {Number} startRow The index of the first row in the range
39877      * @param {Number} endRow The index of the last row in the range
39878      * @param {Boolean} keepExisting (optional) True to retain existing selections
39879      */
39880     selectRange : function(startRow, endRow, keepExisting){
39881         if(this.locked) {
39882             return;
39883         }
39884         if(!keepExisting){
39885             this.clearSelections();
39886         }
39887         if(startRow <= endRow){
39888             for(var i = startRow; i <= endRow; i++){
39889                 this.selectRow(i, true);
39890             }
39891         }else{
39892             for(var i = startRow; i >= endRow; i--){
39893                 this.selectRow(i, true);
39894             }
39895         }
39896     },
39897
39898     /**
39899      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39900      * @param {Number} startRow The index of the first row in the range
39901      * @param {Number} endRow The index of the last row in the range
39902      */
39903     deselectRange : function(startRow, endRow, preventViewNotify){
39904         if(this.locked) {
39905             return;
39906         }
39907         for(var i = startRow; i <= endRow; i++){
39908             this.deselectRow(i, preventViewNotify);
39909         }
39910     },
39911
39912     /**
39913      * Selects a row.
39914      * @param {Number} row The index of the row to select
39915      * @param {Boolean} keepExisting (optional) True to keep existing selections
39916      */
39917     selectRow : function(index, keepExisting, preventViewNotify){
39918         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39919             return;
39920         }
39921         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39922             if(!keepExisting || this.singleSelect){
39923                 this.clearSelections();
39924             }
39925             var r = this.grid.dataSource.getAt(index);
39926             this.selections.add(r);
39927             this.last = this.lastActive = index;
39928             if(!preventViewNotify){
39929                 this.grid.getView().onRowSelect(index);
39930             }
39931             this.fireEvent("rowselect", this, index, r);
39932             this.fireEvent("selectionchange", this);
39933         }
39934     },
39935
39936     /**
39937      * Deselects a row.
39938      * @param {Number} row The index of the row to deselect
39939      */
39940     deselectRow : function(index, preventViewNotify){
39941         if(this.locked) {
39942             return;
39943         }
39944         if(this.last == index){
39945             this.last = false;
39946         }
39947         if(this.lastActive == index){
39948             this.lastActive = false;
39949         }
39950         var r = this.grid.dataSource.getAt(index);
39951         this.selections.remove(r);
39952         if(!preventViewNotify){
39953             this.grid.getView().onRowDeselect(index);
39954         }
39955         this.fireEvent("rowdeselect", this, index);
39956         this.fireEvent("selectionchange", this);
39957     },
39958
39959     // private
39960     restoreLast : function(){
39961         if(this._last){
39962             this.last = this._last;
39963         }
39964     },
39965
39966     // private
39967     acceptsNav : function(row, col, cm){
39968         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39969     },
39970
39971     // private
39972     onEditorKey : function(field, e){
39973         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39974         if(k == e.TAB){
39975             e.stopEvent();
39976             ed.completeEdit();
39977             if(e.shiftKey){
39978                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39979             }else{
39980                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39981             }
39982         }else if(k == e.ENTER && !e.ctrlKey){
39983             e.stopEvent();
39984             ed.completeEdit();
39985             if(e.shiftKey){
39986                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39987             }else{
39988                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39989             }
39990         }else if(k == e.ESC){
39991             ed.cancelEdit();
39992         }
39993         if(newCell){
39994             g.startEditing(newCell[0], newCell[1]);
39995         }
39996     }
39997 });/*
39998  * Based on:
39999  * Ext JS Library 1.1.1
40000  * Copyright(c) 2006-2007, Ext JS, LLC.
40001  *
40002  * Originally Released Under LGPL - original licence link has changed is not relivant.
40003  *
40004  * Fork - LGPL
40005  * <script type="text/javascript">
40006  */
40007 /**
40008  * @class Roo.grid.CellSelectionModel
40009  * @extends Roo.grid.AbstractSelectionModel
40010  * This class provides the basic implementation for cell selection in a grid.
40011  * @constructor
40012  * @param {Object} config The object containing the configuration of this model.
40013  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40014  */
40015 Roo.grid.CellSelectionModel = function(config){
40016     Roo.apply(this, config);
40017
40018     this.selection = null;
40019
40020     this.addEvents({
40021         /**
40022              * @event beforerowselect
40023              * Fires before a cell is selected.
40024              * @param {SelectionModel} this
40025              * @param {Number} rowIndex The selected row index
40026              * @param {Number} colIndex The selected cell index
40027              */
40028             "beforecellselect" : true,
40029         /**
40030              * @event cellselect
40031              * Fires when a cell is selected.
40032              * @param {SelectionModel} this
40033              * @param {Number} rowIndex The selected row index
40034              * @param {Number} colIndex The selected cell index
40035              */
40036             "cellselect" : true,
40037         /**
40038              * @event selectionchange
40039              * Fires when the active selection changes.
40040              * @param {SelectionModel} this
40041              * @param {Object} selection null for no selection or an object (o) with two properties
40042                 <ul>
40043                 <li>o.record: the record object for the row the selection is in</li>
40044                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40045                 </ul>
40046              */
40047             "selectionchange" : true,
40048         /**
40049              * @event tabend
40050              * Fires when the tab (or enter) was pressed on the last editable cell
40051              * You can use this to trigger add new row.
40052              * @param {SelectionModel} this
40053              */
40054             "tabend" : true,
40055          /**
40056              * @event beforeeditnext
40057              * Fires before the next editable sell is made active
40058              * You can use this to skip to another cell or fire the tabend
40059              *    if you set cell to false
40060              * @param {Object} eventdata object : { cell : [ row, col ] } 
40061              */
40062             "beforeeditnext" : true
40063     });
40064     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40065 };
40066
40067 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40068     
40069     enter_is_tab: false,
40070
40071     /** @ignore */
40072     initEvents : function(){
40073         this.grid.on("mousedown", this.handleMouseDown, this);
40074         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40075         var view = this.grid.view;
40076         view.on("refresh", this.onViewChange, this);
40077         view.on("rowupdated", this.onRowUpdated, this);
40078         view.on("beforerowremoved", this.clearSelections, this);
40079         view.on("beforerowsinserted", this.clearSelections, this);
40080         if(this.grid.isEditor){
40081             this.grid.on("beforeedit", this.beforeEdit,  this);
40082         }
40083     },
40084
40085         //private
40086     beforeEdit : function(e){
40087         this.select(e.row, e.column, false, true, e.record);
40088     },
40089
40090         //private
40091     onRowUpdated : function(v, index, r){
40092         if(this.selection && this.selection.record == r){
40093             v.onCellSelect(index, this.selection.cell[1]);
40094         }
40095     },
40096
40097         //private
40098     onViewChange : function(){
40099         this.clearSelections(true);
40100     },
40101
40102         /**
40103          * Returns the currently selected cell,.
40104          * @return {Array} The selected cell (row, column) or null if none selected.
40105          */
40106     getSelectedCell : function(){
40107         return this.selection ? this.selection.cell : null;
40108     },
40109
40110     /**
40111      * Clears all selections.
40112      * @param {Boolean} true to prevent the gridview from being notified about the change.
40113      */
40114     clearSelections : function(preventNotify){
40115         var s = this.selection;
40116         if(s){
40117             if(preventNotify !== true){
40118                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40119             }
40120             this.selection = null;
40121             this.fireEvent("selectionchange", this, null);
40122         }
40123     },
40124
40125     /**
40126      * Returns true if there is a selection.
40127      * @return {Boolean}
40128      */
40129     hasSelection : function(){
40130         return this.selection ? true : false;
40131     },
40132
40133     /** @ignore */
40134     handleMouseDown : function(e, t){
40135         var v = this.grid.getView();
40136         if(this.isLocked()){
40137             return;
40138         };
40139         var row = v.findRowIndex(t);
40140         var cell = v.findCellIndex(t);
40141         if(row !== false && cell !== false){
40142             this.select(row, cell);
40143         }
40144     },
40145
40146     /**
40147      * Selects a cell.
40148      * @param {Number} rowIndex
40149      * @param {Number} collIndex
40150      */
40151     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40152         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40153             this.clearSelections();
40154             r = r || this.grid.dataSource.getAt(rowIndex);
40155             this.selection = {
40156                 record : r,
40157                 cell : [rowIndex, colIndex]
40158             };
40159             if(!preventViewNotify){
40160                 var v = this.grid.getView();
40161                 v.onCellSelect(rowIndex, colIndex);
40162                 if(preventFocus !== true){
40163                     v.focusCell(rowIndex, colIndex);
40164                 }
40165             }
40166             this.fireEvent("cellselect", this, rowIndex, colIndex);
40167             this.fireEvent("selectionchange", this, this.selection);
40168         }
40169     },
40170
40171         //private
40172     isSelectable : function(rowIndex, colIndex, cm){
40173         return !cm.isHidden(colIndex);
40174     },
40175
40176     /** @ignore */
40177     handleKeyDown : function(e){
40178         //Roo.log('Cell Sel Model handleKeyDown');
40179         if(!e.isNavKeyPress()){
40180             return;
40181         }
40182         var g = this.grid, s = this.selection;
40183         if(!s){
40184             e.stopEvent();
40185             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40186             if(cell){
40187                 this.select(cell[0], cell[1]);
40188             }
40189             return;
40190         }
40191         var sm = this;
40192         var walk = function(row, col, step){
40193             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40194         };
40195         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40196         var newCell;
40197
40198       
40199
40200         switch(k){
40201             case e.TAB:
40202                 // handled by onEditorKey
40203                 if (g.isEditor && g.editing) {
40204                     return;
40205                 }
40206                 if(e.shiftKey) {
40207                     newCell = walk(r, c-1, -1);
40208                 } else {
40209                     newCell = walk(r, c+1, 1);
40210                 }
40211                 break;
40212             
40213             case e.DOWN:
40214                newCell = walk(r+1, c, 1);
40215                 break;
40216             
40217             case e.UP:
40218                 newCell = walk(r-1, c, -1);
40219                 break;
40220             
40221             case e.RIGHT:
40222                 newCell = walk(r, c+1, 1);
40223                 break;
40224             
40225             case e.LEFT:
40226                 newCell = walk(r, c-1, -1);
40227                 break;
40228             
40229             case e.ENTER:
40230                 
40231                 if(g.isEditor && !g.editing){
40232                    g.startEditing(r, c);
40233                    e.stopEvent();
40234                    return;
40235                 }
40236                 
40237                 
40238              break;
40239         };
40240         if(newCell){
40241             this.select(newCell[0], newCell[1]);
40242             e.stopEvent();
40243             
40244         }
40245     },
40246
40247     acceptsNav : function(row, col, cm){
40248         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40249     },
40250     /**
40251      * Selects a cell.
40252      * @param {Number} field (not used) - as it's normally used as a listener
40253      * @param {Number} e - event - fake it by using
40254      *
40255      * var e = Roo.EventObjectImpl.prototype;
40256      * e.keyCode = e.TAB
40257      *
40258      * 
40259      */
40260     onEditorKey : function(field, e){
40261         
40262         var k = e.getKey(),
40263             newCell,
40264             g = this.grid,
40265             ed = g.activeEditor,
40266             forward = false;
40267         ///Roo.log('onEditorKey' + k);
40268         
40269         
40270         if (this.enter_is_tab && k == e.ENTER) {
40271             k = e.TAB;
40272         }
40273         
40274         if(k == e.TAB){
40275             if(e.shiftKey){
40276                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40277             }else{
40278                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40279                 forward = true;
40280             }
40281             
40282             e.stopEvent();
40283             
40284         } else if(k == e.ENTER &&  !e.ctrlKey){
40285             ed.completeEdit();
40286             e.stopEvent();
40287             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40288         
40289                 } else if(k == e.ESC){
40290             ed.cancelEdit();
40291         }
40292                 
40293         if (newCell) {
40294             var ecall = { cell : newCell, forward : forward };
40295             this.fireEvent('beforeeditnext', ecall );
40296             newCell = ecall.cell;
40297                         forward = ecall.forward;
40298         }
40299                 
40300         if(newCell){
40301             //Roo.log('next cell after edit');
40302             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40303         } else if (forward) {
40304             // tabbed past last
40305             this.fireEvent.defer(100, this, ['tabend',this]);
40306         }
40307     }
40308 });/*
40309  * Based on:
40310  * Ext JS Library 1.1.1
40311  * Copyright(c) 2006-2007, Ext JS, LLC.
40312  *
40313  * Originally Released Under LGPL - original licence link has changed is not relivant.
40314  *
40315  * Fork - LGPL
40316  * <script type="text/javascript">
40317  */
40318  
40319 /**
40320  * @class Roo.grid.EditorGrid
40321  * @extends Roo.grid.Grid
40322  * Class for creating and editable grid.
40323  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40324  * The container MUST have some type of size defined for the grid to fill. The container will be 
40325  * automatically set to position relative if it isn't already.
40326  * @param {Object} dataSource The data model to bind to
40327  * @param {Object} colModel The column model with info about this grid's columns
40328  */
40329 Roo.grid.EditorGrid = function(container, config){
40330     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40331     this.getGridEl().addClass("xedit-grid");
40332
40333     if(!this.selModel){
40334         this.selModel = new Roo.grid.CellSelectionModel();
40335     }
40336
40337     this.activeEditor = null;
40338
40339         this.addEvents({
40340             /**
40341              * @event beforeedit
40342              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40343              * <ul style="padding:5px;padding-left:16px;">
40344              * <li>grid - This grid</li>
40345              * <li>record - The record being edited</li>
40346              * <li>field - The field name being edited</li>
40347              * <li>value - The value for the field being edited.</li>
40348              * <li>row - The grid row index</li>
40349              * <li>column - The grid column index</li>
40350              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40351              * </ul>
40352              * @param {Object} e An edit event (see above for description)
40353              */
40354             "beforeedit" : true,
40355             /**
40356              * @event afteredit
40357              * Fires after a cell is edited. <br />
40358              * <ul style="padding:5px;padding-left:16px;">
40359              * <li>grid - This grid</li>
40360              * <li>record - The record being edited</li>
40361              * <li>field - The field name being edited</li>
40362              * <li>value - The value being set</li>
40363              * <li>originalValue - The original value for the field, before the edit.</li>
40364              * <li>row - The grid row index</li>
40365              * <li>column - The grid column index</li>
40366              * </ul>
40367              * @param {Object} e An edit event (see above for description)
40368              */
40369             "afteredit" : true,
40370             /**
40371              * @event validateedit
40372              * Fires after a cell is edited, but before the value is set in the record. 
40373          * You can use this to modify the value being set in the field, Return false
40374              * to cancel the change. The edit event object has the following properties <br />
40375              * <ul style="padding:5px;padding-left:16px;">
40376          * <li>editor - This editor</li>
40377              * <li>grid - This grid</li>
40378              * <li>record - The record being edited</li>
40379              * <li>field - The field name being edited</li>
40380              * <li>value - The value being set</li>
40381              * <li>originalValue - The original value for the field, before the edit.</li>
40382              * <li>row - The grid row index</li>
40383              * <li>column - The grid column index</li>
40384              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40385              * </ul>
40386              * @param {Object} e An edit event (see above for description)
40387              */
40388             "validateedit" : true
40389         });
40390     this.on("bodyscroll", this.stopEditing,  this);
40391     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40392 };
40393
40394 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40395     /**
40396      * @cfg {Number} clicksToEdit
40397      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40398      */
40399     clicksToEdit: 2,
40400
40401     // private
40402     isEditor : true,
40403     // private
40404     trackMouseOver: false, // causes very odd FF errors
40405
40406     onCellDblClick : function(g, row, col){
40407         this.startEditing(row, col);
40408     },
40409
40410     onEditComplete : function(ed, value, startValue){
40411         this.editing = false;
40412         this.activeEditor = null;
40413         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40414         var r = ed.record;
40415         var field = this.colModel.getDataIndex(ed.col);
40416         var e = {
40417             grid: this,
40418             record: r,
40419             field: field,
40420             originalValue: startValue,
40421             value: value,
40422             row: ed.row,
40423             column: ed.col,
40424             cancel:false,
40425             editor: ed
40426         };
40427         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40428         cell.show();
40429           
40430         if(String(value) !== String(startValue)){
40431             
40432             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40433                 r.set(field, e.value);
40434                 // if we are dealing with a combo box..
40435                 // then we also set the 'name' colum to be the displayField
40436                 if (ed.field.displayField && ed.field.name) {
40437                     r.set(ed.field.name, ed.field.el.dom.value);
40438                 }
40439                 
40440                 delete e.cancel; //?? why!!!
40441                 this.fireEvent("afteredit", e);
40442             }
40443         } else {
40444             this.fireEvent("afteredit", e); // always fire it!
40445         }
40446         this.view.focusCell(ed.row, ed.col);
40447     },
40448
40449     /**
40450      * Starts editing the specified for the specified row/column
40451      * @param {Number} rowIndex
40452      * @param {Number} colIndex
40453      */
40454     startEditing : function(row, col){
40455         this.stopEditing();
40456         if(this.colModel.isCellEditable(col, row)){
40457             this.view.ensureVisible(row, col, true);
40458           
40459             var r = this.dataSource.getAt(row);
40460             var field = this.colModel.getDataIndex(col);
40461             var cell = Roo.get(this.view.getCell(row,col));
40462             var e = {
40463                 grid: this,
40464                 record: r,
40465                 field: field,
40466                 value: r.data[field],
40467                 row: row,
40468                 column: col,
40469                 cancel:false 
40470             };
40471             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40472                 this.editing = true;
40473                 var ed = this.colModel.getCellEditor(col, row);
40474                 
40475                 if (!ed) {
40476                     return;
40477                 }
40478                 if(!ed.rendered){
40479                     ed.render(ed.parentEl || document.body);
40480                 }
40481                 ed.field.reset();
40482                
40483                 cell.hide();
40484                 
40485                 (function(){ // complex but required for focus issues in safari, ie and opera
40486                     ed.row = row;
40487                     ed.col = col;
40488                     ed.record = r;
40489                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40490                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40491                     this.activeEditor = ed;
40492                     var v = r.data[field];
40493                     ed.startEdit(this.view.getCell(row, col), v);
40494                     // combo's with 'displayField and name set
40495                     if (ed.field.displayField && ed.field.name) {
40496                         ed.field.el.dom.value = r.data[ed.field.name];
40497                     }
40498                     
40499                     
40500                 }).defer(50, this);
40501             }
40502         }
40503     },
40504         
40505     /**
40506      * Stops any active editing
40507      */
40508     stopEditing : function(){
40509         if(this.activeEditor){
40510             this.activeEditor.completeEdit();
40511         }
40512         this.activeEditor = null;
40513     },
40514         
40515          /**
40516      * Called to get grid's drag proxy text, by default returns this.ddText.
40517      * @return {String}
40518      */
40519     getDragDropText : function(){
40520         var count = this.selModel.getSelectedCell() ? 1 : 0;
40521         return String.format(this.ddText, count, count == 1 ? '' : 's');
40522     }
40523         
40524 });/*
40525  * Based on:
40526  * Ext JS Library 1.1.1
40527  * Copyright(c) 2006-2007, Ext JS, LLC.
40528  *
40529  * Originally Released Under LGPL - original licence link has changed is not relivant.
40530  *
40531  * Fork - LGPL
40532  * <script type="text/javascript">
40533  */
40534
40535 // private - not really -- you end up using it !
40536 // This is a support class used internally by the Grid components
40537
40538 /**
40539  * @class Roo.grid.GridEditor
40540  * @extends Roo.Editor
40541  * Class for creating and editable grid elements.
40542  * @param {Object} config any settings (must include field)
40543  */
40544 Roo.grid.GridEditor = function(field, config){
40545     if (!config && field.field) {
40546         config = field;
40547         field = Roo.factory(config.field, Roo.form);
40548     }
40549     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40550     field.monitorTab = false;
40551 };
40552
40553 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40554     
40555     /**
40556      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40557      */
40558     
40559     alignment: "tl-tl",
40560     autoSize: "width",
40561     hideEl : false,
40562     cls: "x-small-editor x-grid-editor",
40563     shim:false,
40564     shadow:"frame"
40565 });/*
40566  * Based on:
40567  * Ext JS Library 1.1.1
40568  * Copyright(c) 2006-2007, Ext JS, LLC.
40569  *
40570  * Originally Released Under LGPL - original licence link has changed is not relivant.
40571  *
40572  * Fork - LGPL
40573  * <script type="text/javascript">
40574  */
40575   
40576
40577   
40578 Roo.grid.PropertyRecord = Roo.data.Record.create([
40579     {name:'name',type:'string'},  'value'
40580 ]);
40581
40582
40583 Roo.grid.PropertyStore = function(grid, source){
40584     this.grid = grid;
40585     this.store = new Roo.data.Store({
40586         recordType : Roo.grid.PropertyRecord
40587     });
40588     this.store.on('update', this.onUpdate,  this);
40589     if(source){
40590         this.setSource(source);
40591     }
40592     Roo.grid.PropertyStore.superclass.constructor.call(this);
40593 };
40594
40595
40596
40597 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40598     setSource : function(o){
40599         this.source = o;
40600         this.store.removeAll();
40601         var data = [];
40602         for(var k in o){
40603             if(this.isEditableValue(o[k])){
40604                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40605             }
40606         }
40607         this.store.loadRecords({records: data}, {}, true);
40608     },
40609
40610     onUpdate : function(ds, record, type){
40611         if(type == Roo.data.Record.EDIT){
40612             var v = record.data['value'];
40613             var oldValue = record.modified['value'];
40614             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40615                 this.source[record.id] = v;
40616                 record.commit();
40617                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40618             }else{
40619                 record.reject();
40620             }
40621         }
40622     },
40623
40624     getProperty : function(row){
40625        return this.store.getAt(row);
40626     },
40627
40628     isEditableValue: function(val){
40629         if(val && val instanceof Date){
40630             return true;
40631         }else if(typeof val == 'object' || typeof val == 'function'){
40632             return false;
40633         }
40634         return true;
40635     },
40636
40637     setValue : function(prop, value){
40638         this.source[prop] = value;
40639         this.store.getById(prop).set('value', value);
40640     },
40641
40642     getSource : function(){
40643         return this.source;
40644     }
40645 });
40646
40647 Roo.grid.PropertyColumnModel = function(grid, store){
40648     this.grid = grid;
40649     var g = Roo.grid;
40650     g.PropertyColumnModel.superclass.constructor.call(this, [
40651         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40652         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40653     ]);
40654     this.store = store;
40655     this.bselect = Roo.DomHelper.append(document.body, {
40656         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40657             {tag: 'option', value: 'true', html: 'true'},
40658             {tag: 'option', value: 'false', html: 'false'}
40659         ]
40660     });
40661     Roo.id(this.bselect);
40662     var f = Roo.form;
40663     this.editors = {
40664         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40665         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40666         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40667         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40668         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40669     };
40670     this.renderCellDelegate = this.renderCell.createDelegate(this);
40671     this.renderPropDelegate = this.renderProp.createDelegate(this);
40672 };
40673
40674 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40675     
40676     
40677     nameText : 'Name',
40678     valueText : 'Value',
40679     
40680     dateFormat : 'm/j/Y',
40681     
40682     
40683     renderDate : function(dateVal){
40684         return dateVal.dateFormat(this.dateFormat);
40685     },
40686
40687     renderBool : function(bVal){
40688         return bVal ? 'true' : 'false';
40689     },
40690
40691     isCellEditable : function(colIndex, rowIndex){
40692         return colIndex == 1;
40693     },
40694
40695     getRenderer : function(col){
40696         return col == 1 ?
40697             this.renderCellDelegate : this.renderPropDelegate;
40698     },
40699
40700     renderProp : function(v){
40701         return this.getPropertyName(v);
40702     },
40703
40704     renderCell : function(val){
40705         var rv = val;
40706         if(val instanceof Date){
40707             rv = this.renderDate(val);
40708         }else if(typeof val == 'boolean'){
40709             rv = this.renderBool(val);
40710         }
40711         return Roo.util.Format.htmlEncode(rv);
40712     },
40713
40714     getPropertyName : function(name){
40715         var pn = this.grid.propertyNames;
40716         return pn && pn[name] ? pn[name] : name;
40717     },
40718
40719     getCellEditor : function(colIndex, rowIndex){
40720         var p = this.store.getProperty(rowIndex);
40721         var n = p.data['name'], val = p.data['value'];
40722         
40723         if(typeof(this.grid.customEditors[n]) == 'string'){
40724             return this.editors[this.grid.customEditors[n]];
40725         }
40726         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40727             return this.grid.customEditors[n];
40728         }
40729         if(val instanceof Date){
40730             return this.editors['date'];
40731         }else if(typeof val == 'number'){
40732             return this.editors['number'];
40733         }else if(typeof val == 'boolean'){
40734             return this.editors['boolean'];
40735         }else{
40736             return this.editors['string'];
40737         }
40738     }
40739 });
40740
40741 /**
40742  * @class Roo.grid.PropertyGrid
40743  * @extends Roo.grid.EditorGrid
40744  * This class represents the  interface of a component based property grid control.
40745  * <br><br>Usage:<pre><code>
40746  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40747       
40748  });
40749  // set any options
40750  grid.render();
40751  * </code></pre>
40752   
40753  * @constructor
40754  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40755  * The container MUST have some type of size defined for the grid to fill. The container will be
40756  * automatically set to position relative if it isn't already.
40757  * @param {Object} config A config object that sets properties on this grid.
40758  */
40759 Roo.grid.PropertyGrid = function(container, config){
40760     config = config || {};
40761     var store = new Roo.grid.PropertyStore(this);
40762     this.store = store;
40763     var cm = new Roo.grid.PropertyColumnModel(this, store);
40764     store.store.sort('name', 'ASC');
40765     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40766         ds: store.store,
40767         cm: cm,
40768         enableColLock:false,
40769         enableColumnMove:false,
40770         stripeRows:false,
40771         trackMouseOver: false,
40772         clicksToEdit:1
40773     }, config));
40774     this.getGridEl().addClass('x-props-grid');
40775     this.lastEditRow = null;
40776     this.on('columnresize', this.onColumnResize, this);
40777     this.addEvents({
40778          /**
40779              * @event beforepropertychange
40780              * Fires before a property changes (return false to stop?)
40781              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40782              * @param {String} id Record Id
40783              * @param {String} newval New Value
40784          * @param {String} oldval Old Value
40785              */
40786         "beforepropertychange": true,
40787         /**
40788              * @event propertychange
40789              * Fires after a property changes
40790              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40791              * @param {String} id Record Id
40792              * @param {String} newval New Value
40793          * @param {String} oldval Old Value
40794              */
40795         "propertychange": true
40796     });
40797     this.customEditors = this.customEditors || {};
40798 };
40799 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40800     
40801      /**
40802      * @cfg {Object} customEditors map of colnames=> custom editors.
40803      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40804      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40805      * false disables editing of the field.
40806          */
40807     
40808       /**
40809      * @cfg {Object} propertyNames map of property Names to their displayed value
40810          */
40811     
40812     render : function(){
40813         Roo.grid.PropertyGrid.superclass.render.call(this);
40814         this.autoSize.defer(100, this);
40815     },
40816
40817     autoSize : function(){
40818         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40819         if(this.view){
40820             this.view.fitColumns();
40821         }
40822     },
40823
40824     onColumnResize : function(){
40825         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40826         this.autoSize();
40827     },
40828     /**
40829      * Sets the data for the Grid
40830      * accepts a Key => Value object of all the elements avaiable.
40831      * @param {Object} data  to appear in grid.
40832      */
40833     setSource : function(source){
40834         this.store.setSource(source);
40835         //this.autoSize();
40836     },
40837     /**
40838      * Gets all the data from the grid.
40839      * @return {Object} data  data stored in grid
40840      */
40841     getSource : function(){
40842         return this.store.getSource();
40843     }
40844 });/*
40845   
40846  * Licence LGPL
40847  
40848  */
40849  
40850 /**
40851  * @class Roo.grid.Calendar
40852  * @extends Roo.util.Grid
40853  * This class extends the Grid to provide a calendar widget
40854  * <br><br>Usage:<pre><code>
40855  var grid = new Roo.grid.Calendar("my-container-id", {
40856      ds: myDataStore,
40857      cm: myColModel,
40858      selModel: mySelectionModel,
40859      autoSizeColumns: true,
40860      monitorWindowResize: false,
40861      trackMouseOver: true
40862      eventstore : real data store..
40863  });
40864  // set any options
40865  grid.render();
40866   
40867   * @constructor
40868  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40869  * The container MUST have some type of size defined for the grid to fill. The container will be
40870  * automatically set to position relative if it isn't already.
40871  * @param {Object} config A config object that sets properties on this grid.
40872  */
40873 Roo.grid.Calendar = function(container, config){
40874         // initialize the container
40875         this.container = Roo.get(container);
40876         this.container.update("");
40877         this.container.setStyle("overflow", "hidden");
40878     this.container.addClass('x-grid-container');
40879
40880     this.id = this.container.id;
40881
40882     Roo.apply(this, config);
40883     // check and correct shorthanded configs
40884     
40885     var rows = [];
40886     var d =1;
40887     for (var r = 0;r < 6;r++) {
40888         
40889         rows[r]=[];
40890         for (var c =0;c < 7;c++) {
40891             rows[r][c]= '';
40892         }
40893     }
40894     if (this.eventStore) {
40895         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40896         this.eventStore.on('load',this.onLoad, this);
40897         this.eventStore.on('beforeload',this.clearEvents, this);
40898          
40899     }
40900     
40901     this.dataSource = new Roo.data.Store({
40902             proxy: new Roo.data.MemoryProxy(rows),
40903             reader: new Roo.data.ArrayReader({}, [
40904                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40905     });
40906
40907     this.dataSource.load();
40908     this.ds = this.dataSource;
40909     this.ds.xmodule = this.xmodule || false;
40910     
40911     
40912     var cellRender = function(v,x,r)
40913     {
40914         return String.format(
40915             '<div class="fc-day  fc-widget-content"><div>' +
40916                 '<div class="fc-event-container"></div>' +
40917                 '<div class="fc-day-number">{0}</div>'+
40918                 
40919                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40920             '</div></div>', v);
40921     
40922     }
40923     
40924     
40925     this.colModel = new Roo.grid.ColumnModel( [
40926         {
40927             xtype: 'ColumnModel',
40928             xns: Roo.grid,
40929             dataIndex : 'weekday0',
40930             header : 'Sunday',
40931             renderer : cellRender
40932         },
40933         {
40934             xtype: 'ColumnModel',
40935             xns: Roo.grid,
40936             dataIndex : 'weekday1',
40937             header : 'Monday',
40938             renderer : cellRender
40939         },
40940         {
40941             xtype: 'ColumnModel',
40942             xns: Roo.grid,
40943             dataIndex : 'weekday2',
40944             header : 'Tuesday',
40945             renderer : cellRender
40946         },
40947         {
40948             xtype: 'ColumnModel',
40949             xns: Roo.grid,
40950             dataIndex : 'weekday3',
40951             header : 'Wednesday',
40952             renderer : cellRender
40953         },
40954         {
40955             xtype: 'ColumnModel',
40956             xns: Roo.grid,
40957             dataIndex : 'weekday4',
40958             header : 'Thursday',
40959             renderer : cellRender
40960         },
40961         {
40962             xtype: 'ColumnModel',
40963             xns: Roo.grid,
40964             dataIndex : 'weekday5',
40965             header : 'Friday',
40966             renderer : cellRender
40967         },
40968         {
40969             xtype: 'ColumnModel',
40970             xns: Roo.grid,
40971             dataIndex : 'weekday6',
40972             header : 'Saturday',
40973             renderer : cellRender
40974         }
40975     ]);
40976     this.cm = this.colModel;
40977     this.cm.xmodule = this.xmodule || false;
40978  
40979         
40980           
40981     //this.selModel = new Roo.grid.CellSelectionModel();
40982     //this.sm = this.selModel;
40983     //this.selModel.init(this);
40984     
40985     
40986     if(this.width){
40987         this.container.setWidth(this.width);
40988     }
40989
40990     if(this.height){
40991         this.container.setHeight(this.height);
40992     }
40993     /** @private */
40994         this.addEvents({
40995         // raw events
40996         /**
40997          * @event click
40998          * The raw click event for the entire grid.
40999          * @param {Roo.EventObject} e
41000          */
41001         "click" : true,
41002         /**
41003          * @event dblclick
41004          * The raw dblclick event for the entire grid.
41005          * @param {Roo.EventObject} e
41006          */
41007         "dblclick" : true,
41008         /**
41009          * @event contextmenu
41010          * The raw contextmenu event for the entire grid.
41011          * @param {Roo.EventObject} e
41012          */
41013         "contextmenu" : true,
41014         /**
41015          * @event mousedown
41016          * The raw mousedown event for the entire grid.
41017          * @param {Roo.EventObject} e
41018          */
41019         "mousedown" : true,
41020         /**
41021          * @event mouseup
41022          * The raw mouseup event for the entire grid.
41023          * @param {Roo.EventObject} e
41024          */
41025         "mouseup" : true,
41026         /**
41027          * @event mouseover
41028          * The raw mouseover event for the entire grid.
41029          * @param {Roo.EventObject} e
41030          */
41031         "mouseover" : true,
41032         /**
41033          * @event mouseout
41034          * The raw mouseout event for the entire grid.
41035          * @param {Roo.EventObject} e
41036          */
41037         "mouseout" : true,
41038         /**
41039          * @event keypress
41040          * The raw keypress event for the entire grid.
41041          * @param {Roo.EventObject} e
41042          */
41043         "keypress" : true,
41044         /**
41045          * @event keydown
41046          * The raw keydown event for the entire grid.
41047          * @param {Roo.EventObject} e
41048          */
41049         "keydown" : true,
41050
41051         // custom events
41052
41053         /**
41054          * @event cellclick
41055          * Fires when a cell is clicked
41056          * @param {Grid} this
41057          * @param {Number} rowIndex
41058          * @param {Number} columnIndex
41059          * @param {Roo.EventObject} e
41060          */
41061         "cellclick" : true,
41062         /**
41063          * @event celldblclick
41064          * Fires when a cell is double clicked
41065          * @param {Grid} this
41066          * @param {Number} rowIndex
41067          * @param {Number} columnIndex
41068          * @param {Roo.EventObject} e
41069          */
41070         "celldblclick" : true,
41071         /**
41072          * @event rowclick
41073          * Fires when a row is clicked
41074          * @param {Grid} this
41075          * @param {Number} rowIndex
41076          * @param {Roo.EventObject} e
41077          */
41078         "rowclick" : true,
41079         /**
41080          * @event rowdblclick
41081          * Fires when a row is double clicked
41082          * @param {Grid} this
41083          * @param {Number} rowIndex
41084          * @param {Roo.EventObject} e
41085          */
41086         "rowdblclick" : true,
41087         /**
41088          * @event headerclick
41089          * Fires when a header is clicked
41090          * @param {Grid} this
41091          * @param {Number} columnIndex
41092          * @param {Roo.EventObject} e
41093          */
41094         "headerclick" : true,
41095         /**
41096          * @event headerdblclick
41097          * Fires when a header cell is double clicked
41098          * @param {Grid} this
41099          * @param {Number} columnIndex
41100          * @param {Roo.EventObject} e
41101          */
41102         "headerdblclick" : true,
41103         /**
41104          * @event rowcontextmenu
41105          * Fires when a row is right clicked
41106          * @param {Grid} this
41107          * @param {Number} rowIndex
41108          * @param {Roo.EventObject} e
41109          */
41110         "rowcontextmenu" : true,
41111         /**
41112          * @event cellcontextmenu
41113          * Fires when a cell is right clicked
41114          * @param {Grid} this
41115          * @param {Number} rowIndex
41116          * @param {Number} cellIndex
41117          * @param {Roo.EventObject} e
41118          */
41119          "cellcontextmenu" : true,
41120         /**
41121          * @event headercontextmenu
41122          * Fires when a header is right clicked
41123          * @param {Grid} this
41124          * @param {Number} columnIndex
41125          * @param {Roo.EventObject} e
41126          */
41127         "headercontextmenu" : true,
41128         /**
41129          * @event bodyscroll
41130          * Fires when the body element is scrolled
41131          * @param {Number} scrollLeft
41132          * @param {Number} scrollTop
41133          */
41134         "bodyscroll" : true,
41135         /**
41136          * @event columnresize
41137          * Fires when the user resizes a column
41138          * @param {Number} columnIndex
41139          * @param {Number} newSize
41140          */
41141         "columnresize" : true,
41142         /**
41143          * @event columnmove
41144          * Fires when the user moves a column
41145          * @param {Number} oldIndex
41146          * @param {Number} newIndex
41147          */
41148         "columnmove" : true,
41149         /**
41150          * @event startdrag
41151          * Fires when row(s) start being dragged
41152          * @param {Grid} this
41153          * @param {Roo.GridDD} dd The drag drop object
41154          * @param {event} e The raw browser event
41155          */
41156         "startdrag" : true,
41157         /**
41158          * @event enddrag
41159          * Fires when a drag operation is complete
41160          * @param {Grid} this
41161          * @param {Roo.GridDD} dd The drag drop object
41162          * @param {event} e The raw browser event
41163          */
41164         "enddrag" : true,
41165         /**
41166          * @event dragdrop
41167          * Fires when dragged row(s) are dropped on a valid DD target
41168          * @param {Grid} this
41169          * @param {Roo.GridDD} dd The drag drop object
41170          * @param {String} targetId The target drag drop object
41171          * @param {event} e The raw browser event
41172          */
41173         "dragdrop" : true,
41174         /**
41175          * @event dragover
41176          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41177          * @param {Grid} this
41178          * @param {Roo.GridDD} dd The drag drop object
41179          * @param {String} targetId The target drag drop object
41180          * @param {event} e The raw browser event
41181          */
41182         "dragover" : true,
41183         /**
41184          * @event dragenter
41185          *  Fires when the dragged row(s) first cross another DD target while being dragged
41186          * @param {Grid} this
41187          * @param {Roo.GridDD} dd The drag drop object
41188          * @param {String} targetId The target drag drop object
41189          * @param {event} e The raw browser event
41190          */
41191         "dragenter" : true,
41192         /**
41193          * @event dragout
41194          * Fires when the dragged row(s) leave another DD target while being dragged
41195          * @param {Grid} this
41196          * @param {Roo.GridDD} dd The drag drop object
41197          * @param {String} targetId The target drag drop object
41198          * @param {event} e The raw browser event
41199          */
41200         "dragout" : true,
41201         /**
41202          * @event rowclass
41203          * Fires when a row is rendered, so you can change add a style to it.
41204          * @param {GridView} gridview   The grid view
41205          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41206          */
41207         'rowclass' : true,
41208
41209         /**
41210          * @event render
41211          * Fires when the grid is rendered
41212          * @param {Grid} grid
41213          */
41214         'render' : true,
41215             /**
41216              * @event select
41217              * Fires when a date is selected
41218              * @param {DatePicker} this
41219              * @param {Date} date The selected date
41220              */
41221         'select': true,
41222         /**
41223              * @event monthchange
41224              * Fires when the displayed month changes 
41225              * @param {DatePicker} this
41226              * @param {Date} date The selected month
41227              */
41228         'monthchange': true,
41229         /**
41230              * @event evententer
41231              * Fires when mouse over an event
41232              * @param {Calendar} this
41233              * @param {event} Event
41234              */
41235         'evententer': true,
41236         /**
41237              * @event eventleave
41238              * Fires when the mouse leaves an
41239              * @param {Calendar} this
41240              * @param {event}
41241              */
41242         'eventleave': true,
41243         /**
41244              * @event eventclick
41245              * Fires when the mouse click an
41246              * @param {Calendar} this
41247              * @param {event}
41248              */
41249         'eventclick': true,
41250         /**
41251              * @event eventrender
41252              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41253              * @param {Calendar} this
41254              * @param {data} data to be modified
41255              */
41256         'eventrender': true
41257         
41258     });
41259
41260     Roo.grid.Grid.superclass.constructor.call(this);
41261     this.on('render', function() {
41262         this.view.el.addClass('x-grid-cal'); 
41263         
41264         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41265
41266     },this);
41267     
41268     if (!Roo.grid.Calendar.style) {
41269         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41270             
41271             
41272             '.x-grid-cal .x-grid-col' :  {
41273                 height: 'auto !important',
41274                 'vertical-align': 'top'
41275             },
41276             '.x-grid-cal  .fc-event-hori' : {
41277                 height: '14px'
41278             }
41279              
41280             
41281         }, Roo.id());
41282     }
41283
41284     
41285     
41286 };
41287 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41288     /**
41289      * @cfg {Store} eventStore The store that loads events.
41290      */
41291     eventStore : 25,
41292
41293      
41294     activeDate : false,
41295     startDay : 0,
41296     autoWidth : true,
41297     monitorWindowResize : false,
41298
41299     
41300     resizeColumns : function() {
41301         var col = (this.view.el.getWidth() / 7) - 3;
41302         // loop through cols, and setWidth
41303         for(var i =0 ; i < 7 ; i++){
41304             this.cm.setColumnWidth(i, col);
41305         }
41306     },
41307      setDate :function(date) {
41308         
41309         Roo.log('setDate?');
41310         
41311         this.resizeColumns();
41312         var vd = this.activeDate;
41313         this.activeDate = date;
41314 //        if(vd && this.el){
41315 //            var t = date.getTime();
41316 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41317 //                Roo.log('using add remove');
41318 //                
41319 //                this.fireEvent('monthchange', this, date);
41320 //                
41321 //                this.cells.removeClass("fc-state-highlight");
41322 //                this.cells.each(function(c){
41323 //                   if(c.dateValue == t){
41324 //                       c.addClass("fc-state-highlight");
41325 //                       setTimeout(function(){
41326 //                            try{c.dom.firstChild.focus();}catch(e){}
41327 //                       }, 50);
41328 //                       return false;
41329 //                   }
41330 //                   return true;
41331 //                });
41332 //                return;
41333 //            }
41334 //        }
41335         
41336         var days = date.getDaysInMonth();
41337         
41338         var firstOfMonth = date.getFirstDateOfMonth();
41339         var startingPos = firstOfMonth.getDay()-this.startDay;
41340         
41341         if(startingPos < this.startDay){
41342             startingPos += 7;
41343         }
41344         
41345         var pm = date.add(Date.MONTH, -1);
41346         var prevStart = pm.getDaysInMonth()-startingPos;
41347 //        
41348         
41349         
41350         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41351         
41352         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41353         //this.cells.addClassOnOver('fc-state-hover');
41354         
41355         var cells = this.cells.elements;
41356         var textEls = this.textNodes;
41357         
41358         //Roo.each(cells, function(cell){
41359         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41360         //});
41361         
41362         days += startingPos;
41363
41364         // convert everything to numbers so it's fast
41365         var day = 86400000;
41366         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41367         //Roo.log(d);
41368         //Roo.log(pm);
41369         //Roo.log(prevStart);
41370         
41371         var today = new Date().clearTime().getTime();
41372         var sel = date.clearTime().getTime();
41373         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41374         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41375         var ddMatch = this.disabledDatesRE;
41376         var ddText = this.disabledDatesText;
41377         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41378         var ddaysText = this.disabledDaysText;
41379         var format = this.format;
41380         
41381         var setCellClass = function(cal, cell){
41382             
41383             //Roo.log('set Cell Class');
41384             cell.title = "";
41385             var t = d.getTime();
41386             
41387             //Roo.log(d);
41388             
41389             
41390             cell.dateValue = t;
41391             if(t == today){
41392                 cell.className += " fc-today";
41393                 cell.className += " fc-state-highlight";
41394                 cell.title = cal.todayText;
41395             }
41396             if(t == sel){
41397                 // disable highlight in other month..
41398                 cell.className += " fc-state-highlight";
41399                 
41400             }
41401             // disabling
41402             if(t < min) {
41403                 //cell.className = " fc-state-disabled";
41404                 cell.title = cal.minText;
41405                 return;
41406             }
41407             if(t > max) {
41408                 //cell.className = " fc-state-disabled";
41409                 cell.title = cal.maxText;
41410                 return;
41411             }
41412             if(ddays){
41413                 if(ddays.indexOf(d.getDay()) != -1){
41414                     // cell.title = ddaysText;
41415                    // cell.className = " fc-state-disabled";
41416                 }
41417             }
41418             if(ddMatch && format){
41419                 var fvalue = d.dateFormat(format);
41420                 if(ddMatch.test(fvalue)){
41421                     cell.title = ddText.replace("%0", fvalue);
41422                    cell.className = " fc-state-disabled";
41423                 }
41424             }
41425             
41426             if (!cell.initialClassName) {
41427                 cell.initialClassName = cell.dom.className;
41428             }
41429             
41430             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41431         };
41432
41433         var i = 0;
41434         
41435         for(; i < startingPos; i++) {
41436             cells[i].dayName =  (++prevStart);
41437             Roo.log(textEls[i]);
41438             d.setDate(d.getDate()+1);
41439             
41440             //cells[i].className = "fc-past fc-other-month";
41441             setCellClass(this, cells[i]);
41442         }
41443         
41444         var intDay = 0;
41445         
41446         for(; i < days; i++){
41447             intDay = i - startingPos + 1;
41448             cells[i].dayName =  (intDay);
41449             d.setDate(d.getDate()+1);
41450             
41451             cells[i].className = ''; // "x-date-active";
41452             setCellClass(this, cells[i]);
41453         }
41454         var extraDays = 0;
41455         
41456         for(; i < 42; i++) {
41457             //textEls[i].innerHTML = (++extraDays);
41458             
41459             d.setDate(d.getDate()+1);
41460             cells[i].dayName = (++extraDays);
41461             cells[i].className = "fc-future fc-other-month";
41462             setCellClass(this, cells[i]);
41463         }
41464         
41465         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41466         
41467         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41468         
41469         // this will cause all the cells to mis
41470         var rows= [];
41471         var i =0;
41472         for (var r = 0;r < 6;r++) {
41473             for (var c =0;c < 7;c++) {
41474                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41475             }    
41476         }
41477         
41478         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41479         for(i=0;i<cells.length;i++) {
41480             
41481             this.cells.elements[i].dayName = cells[i].dayName ;
41482             this.cells.elements[i].className = cells[i].className;
41483             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41484             this.cells.elements[i].title = cells[i].title ;
41485             this.cells.elements[i].dateValue = cells[i].dateValue ;
41486         }
41487         
41488         
41489         
41490         
41491         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41492         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41493         
41494         ////if(totalRows != 6){
41495             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41496            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41497        // }
41498         
41499         this.fireEvent('monthchange', this, date);
41500         
41501         
41502     },
41503  /**
41504      * Returns the grid's SelectionModel.
41505      * @return {SelectionModel}
41506      */
41507     getSelectionModel : function(){
41508         if(!this.selModel){
41509             this.selModel = new Roo.grid.CellSelectionModel();
41510         }
41511         return this.selModel;
41512     },
41513
41514     load: function() {
41515         this.eventStore.load()
41516         
41517         
41518         
41519     },
41520     
41521     findCell : function(dt) {
41522         dt = dt.clearTime().getTime();
41523         var ret = false;
41524         this.cells.each(function(c){
41525             //Roo.log("check " +c.dateValue + '?=' + dt);
41526             if(c.dateValue == dt){
41527                 ret = c;
41528                 return false;
41529             }
41530             return true;
41531         });
41532         
41533         return ret;
41534     },
41535     
41536     findCells : function(rec) {
41537         var s = rec.data.start_dt.clone().clearTime().getTime();
41538        // Roo.log(s);
41539         var e= rec.data.end_dt.clone().clearTime().getTime();
41540        // Roo.log(e);
41541         var ret = [];
41542         this.cells.each(function(c){
41543              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41544             
41545             if(c.dateValue > e){
41546                 return ;
41547             }
41548             if(c.dateValue < s){
41549                 return ;
41550             }
41551             ret.push(c);
41552         });
41553         
41554         return ret;    
41555     },
41556     
41557     findBestRow: function(cells)
41558     {
41559         var ret = 0;
41560         
41561         for (var i =0 ; i < cells.length;i++) {
41562             ret  = Math.max(cells[i].rows || 0,ret);
41563         }
41564         return ret;
41565         
41566     },
41567     
41568     
41569     addItem : function(rec)
41570     {
41571         // look for vertical location slot in
41572         var cells = this.findCells(rec);
41573         
41574         rec.row = this.findBestRow(cells);
41575         
41576         // work out the location.
41577         
41578         var crow = false;
41579         var rows = [];
41580         for(var i =0; i < cells.length; i++) {
41581             if (!crow) {
41582                 crow = {
41583                     start : cells[i],
41584                     end :  cells[i]
41585                 };
41586                 continue;
41587             }
41588             if (crow.start.getY() == cells[i].getY()) {
41589                 // on same row.
41590                 crow.end = cells[i];
41591                 continue;
41592             }
41593             // different row.
41594             rows.push(crow);
41595             crow = {
41596                 start: cells[i],
41597                 end : cells[i]
41598             };
41599             
41600         }
41601         
41602         rows.push(crow);
41603         rec.els = [];
41604         rec.rows = rows;
41605         rec.cells = cells;
41606         for (var i = 0; i < cells.length;i++) {
41607             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41608             
41609         }
41610         
41611         
41612     },
41613     
41614     clearEvents: function() {
41615         
41616         if (!this.eventStore.getCount()) {
41617             return;
41618         }
41619         // reset number of rows in cells.
41620         Roo.each(this.cells.elements, function(c){
41621             c.rows = 0;
41622         });
41623         
41624         this.eventStore.each(function(e) {
41625             this.clearEvent(e);
41626         },this);
41627         
41628     },
41629     
41630     clearEvent : function(ev)
41631     {
41632         if (ev.els) {
41633             Roo.each(ev.els, function(el) {
41634                 el.un('mouseenter' ,this.onEventEnter, this);
41635                 el.un('mouseleave' ,this.onEventLeave, this);
41636                 el.remove();
41637             },this);
41638             ev.els = [];
41639         }
41640     },
41641     
41642     
41643     renderEvent : function(ev,ctr) {
41644         if (!ctr) {
41645              ctr = this.view.el.select('.fc-event-container',true).first();
41646         }
41647         
41648          
41649         this.clearEvent(ev);
41650             //code
41651        
41652         
41653         
41654         ev.els = [];
41655         var cells = ev.cells;
41656         var rows = ev.rows;
41657         this.fireEvent('eventrender', this, ev);
41658         
41659         for(var i =0; i < rows.length; i++) {
41660             
41661             cls = '';
41662             if (i == 0) {
41663                 cls += ' fc-event-start';
41664             }
41665             if ((i+1) == rows.length) {
41666                 cls += ' fc-event-end';
41667             }
41668             
41669             //Roo.log(ev.data);
41670             // how many rows should it span..
41671             var cg = this.eventTmpl.append(ctr,Roo.apply({
41672                 fccls : cls
41673                 
41674             }, ev.data) , true);
41675             
41676             
41677             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41678             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41679             cg.on('click', this.onEventClick, this, ev);
41680             
41681             ev.els.push(cg);
41682             
41683             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41684             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41685             //Roo.log(cg);
41686              
41687             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41688             cg.setWidth(ebox.right - sbox.x -2);
41689         }
41690     },
41691     
41692     renderEvents: function()
41693     {   
41694         // first make sure there is enough space..
41695         
41696         if (!this.eventTmpl) {
41697             this.eventTmpl = new Roo.Template(
41698                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41699                     '<div class="fc-event-inner">' +
41700                         '<span class="fc-event-time">{time}</span>' +
41701                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41702                     '</div>' +
41703                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41704                 '</div>'
41705             );
41706                 
41707         }
41708                
41709         
41710         
41711         this.cells.each(function(c) {
41712             //Roo.log(c.select('.fc-day-content div',true).first());
41713             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41714         });
41715         
41716         var ctr = this.view.el.select('.fc-event-container',true).first();
41717         
41718         var cls;
41719         this.eventStore.each(function(ev){
41720             
41721             this.renderEvent(ev);
41722              
41723              
41724         }, this);
41725         this.view.layout();
41726         
41727     },
41728     
41729     onEventEnter: function (e, el,event,d) {
41730         this.fireEvent('evententer', this, el, event);
41731     },
41732     
41733     onEventLeave: function (e, el,event,d) {
41734         this.fireEvent('eventleave', this, el, event);
41735     },
41736     
41737     onEventClick: function (e, el,event,d) {
41738         this.fireEvent('eventclick', this, el, event);
41739     },
41740     
41741     onMonthChange: function () {
41742         this.store.load();
41743     },
41744     
41745     onLoad: function () {
41746         
41747         //Roo.log('calendar onload');
41748 //         
41749         if(this.eventStore.getCount() > 0){
41750             
41751            
41752             
41753             this.eventStore.each(function(d){
41754                 
41755                 
41756                 // FIXME..
41757                 var add =   d.data;
41758                 if (typeof(add.end_dt) == 'undefined')  {
41759                     Roo.log("Missing End time in calendar data: ");
41760                     Roo.log(d);
41761                     return;
41762                 }
41763                 if (typeof(add.start_dt) == 'undefined')  {
41764                     Roo.log("Missing Start time in calendar data: ");
41765                     Roo.log(d);
41766                     return;
41767                 }
41768                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41769                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41770                 add.id = add.id || d.id;
41771                 add.title = add.title || '??';
41772                 
41773                 this.addItem(d);
41774                 
41775              
41776             },this);
41777         }
41778         
41779         this.renderEvents();
41780     }
41781     
41782
41783 });
41784 /*
41785  grid : {
41786                 xtype: 'Grid',
41787                 xns: Roo.grid,
41788                 listeners : {
41789                     render : function ()
41790                     {
41791                         _this.grid = this;
41792                         
41793                         if (!this.view.el.hasClass('course-timesheet')) {
41794                             this.view.el.addClass('course-timesheet');
41795                         }
41796                         if (this.tsStyle) {
41797                             this.ds.load({});
41798                             return; 
41799                         }
41800                         Roo.log('width');
41801                         Roo.log(_this.grid.view.el.getWidth());
41802                         
41803                         
41804                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41805                             '.course-timesheet .x-grid-row' : {
41806                                 height: '80px'
41807                             },
41808                             '.x-grid-row td' : {
41809                                 'vertical-align' : 0
41810                             },
41811                             '.course-edit-link' : {
41812                                 'color' : 'blue',
41813                                 'text-overflow' : 'ellipsis',
41814                                 'overflow' : 'hidden',
41815                                 'white-space' : 'nowrap',
41816                                 'cursor' : 'pointer'
41817                             },
41818                             '.sub-link' : {
41819                                 'color' : 'green'
41820                             },
41821                             '.de-act-sup-link' : {
41822                                 'color' : 'purple',
41823                                 'text-decoration' : 'line-through'
41824                             },
41825                             '.de-act-link' : {
41826                                 'color' : 'red',
41827                                 'text-decoration' : 'line-through'
41828                             },
41829                             '.course-timesheet .course-highlight' : {
41830                                 'border-top-style': 'dashed !important',
41831                                 'border-bottom-bottom': 'dashed !important'
41832                             },
41833                             '.course-timesheet .course-item' : {
41834                                 'font-family'   : 'tahoma, arial, helvetica',
41835                                 'font-size'     : '11px',
41836                                 'overflow'      : 'hidden',
41837                                 'padding-left'  : '10px',
41838                                 'padding-right' : '10px',
41839                                 'padding-top' : '10px' 
41840                             }
41841                             
41842                         }, Roo.id());
41843                                 this.ds.load({});
41844                     }
41845                 },
41846                 autoWidth : true,
41847                 monitorWindowResize : false,
41848                 cellrenderer : function(v,x,r)
41849                 {
41850                     return v;
41851                 },
41852                 sm : {
41853                     xtype: 'CellSelectionModel',
41854                     xns: Roo.grid
41855                 },
41856                 dataSource : {
41857                     xtype: 'Store',
41858                     xns: Roo.data,
41859                     listeners : {
41860                         beforeload : function (_self, options)
41861                         {
41862                             options.params = options.params || {};
41863                             options.params._month = _this.monthField.getValue();
41864                             options.params.limit = 9999;
41865                             options.params['sort'] = 'when_dt';    
41866                             options.params['dir'] = 'ASC';    
41867                             this.proxy.loadResponse = this.loadResponse;
41868                             Roo.log("load?");
41869                             //this.addColumns();
41870                         },
41871                         load : function (_self, records, options)
41872                         {
41873                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41874                                 // if you click on the translation.. you can edit it...
41875                                 var el = Roo.get(this);
41876                                 var id = el.dom.getAttribute('data-id');
41877                                 var d = el.dom.getAttribute('data-date');
41878                                 var t = el.dom.getAttribute('data-time');
41879                                 //var id = this.child('span').dom.textContent;
41880                                 
41881                                 //Roo.log(this);
41882                                 Pman.Dialog.CourseCalendar.show({
41883                                     id : id,
41884                                     when_d : d,
41885                                     when_t : t,
41886                                     productitem_active : id ? 1 : 0
41887                                 }, function() {
41888                                     _this.grid.ds.load({});
41889                                 });
41890                            
41891                            });
41892                            
41893                            _this.panel.fireEvent('resize', [ '', '' ]);
41894                         }
41895                     },
41896                     loadResponse : function(o, success, response){
41897                             // this is overridden on before load..
41898                             
41899                             Roo.log("our code?");       
41900                             //Roo.log(success);
41901                             //Roo.log(response)
41902                             delete this.activeRequest;
41903                             if(!success){
41904                                 this.fireEvent("loadexception", this, o, response);
41905                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41906                                 return;
41907                             }
41908                             var result;
41909                             try {
41910                                 result = o.reader.read(response);
41911                             }catch(e){
41912                                 Roo.log("load exception?");
41913                                 this.fireEvent("loadexception", this, o, response, e);
41914                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41915                                 return;
41916                             }
41917                             Roo.log("ready...");        
41918                             // loop through result.records;
41919                             // and set this.tdate[date] = [] << array of records..
41920                             _this.tdata  = {};
41921                             Roo.each(result.records, function(r){
41922                                 //Roo.log(r.data);
41923                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41924                                     _this.tdata[r.data.when_dt.format('j')] = [];
41925                                 }
41926                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41927                             });
41928                             
41929                             //Roo.log(_this.tdata);
41930                             
41931                             result.records = [];
41932                             result.totalRecords = 6;
41933                     
41934                             // let's generate some duumy records for the rows.
41935                             //var st = _this.dateField.getValue();
41936                             
41937                             // work out monday..
41938                             //st = st.add(Date.DAY, -1 * st.format('w'));
41939                             
41940                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41941                             
41942                             var firstOfMonth = date.getFirstDayOfMonth();
41943                             var days = date.getDaysInMonth();
41944                             var d = 1;
41945                             var firstAdded = false;
41946                             for (var i = 0; i < result.totalRecords ; i++) {
41947                                 //var d= st.add(Date.DAY, i);
41948                                 var row = {};
41949                                 var added = 0;
41950                                 for(var w = 0 ; w < 7 ; w++){
41951                                     if(!firstAdded && firstOfMonth != w){
41952                                         continue;
41953                                     }
41954                                     if(d > days){
41955                                         continue;
41956                                     }
41957                                     firstAdded = true;
41958                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41959                                     row['weekday'+w] = String.format(
41960                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41961                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41962                                                     d,
41963                                                     date.format('Y-m-')+dd
41964                                                 );
41965                                     added++;
41966                                     if(typeof(_this.tdata[d]) != 'undefined'){
41967                                         Roo.each(_this.tdata[d], function(r){
41968                                             var is_sub = '';
41969                                             var deactive = '';
41970                                             var id = r.id;
41971                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41972                                             if(r.parent_id*1>0){
41973                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41974                                                 id = r.parent_id;
41975                                             }
41976                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41977                                                 deactive = 'de-act-link';
41978                                             }
41979                                             
41980                                             row['weekday'+w] += String.format(
41981                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41982                                                     id, //0
41983                                                     r.product_id_name, //1
41984                                                     r.when_dt.format('h:ia'), //2
41985                                                     is_sub, //3
41986                                                     deactive, //4
41987                                                     desc // 5
41988                                             );
41989                                         });
41990                                     }
41991                                     d++;
41992                                 }
41993                                 
41994                                 // only do this if something added..
41995                                 if(added > 0){ 
41996                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41997                                 }
41998                                 
41999                                 
42000                                 // push it twice. (second one with an hour..
42001                                 
42002                             }
42003                             //Roo.log(result);
42004                             this.fireEvent("load", this, o, o.request.arg);
42005                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42006                         },
42007                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42008                     proxy : {
42009                         xtype: 'HttpProxy',
42010                         xns: Roo.data,
42011                         method : 'GET',
42012                         url : baseURL + '/Roo/Shop_course.php'
42013                     },
42014                     reader : {
42015                         xtype: 'JsonReader',
42016                         xns: Roo.data,
42017                         id : 'id',
42018                         fields : [
42019                             {
42020                                 'name': 'id',
42021                                 'type': 'int'
42022                             },
42023                             {
42024                                 'name': 'when_dt',
42025                                 'type': 'string'
42026                             },
42027                             {
42028                                 'name': 'end_dt',
42029                                 'type': 'string'
42030                             },
42031                             {
42032                                 'name': 'parent_id',
42033                                 'type': 'int'
42034                             },
42035                             {
42036                                 'name': 'product_id',
42037                                 'type': 'int'
42038                             },
42039                             {
42040                                 'name': 'productitem_id',
42041                                 'type': 'int'
42042                             },
42043                             {
42044                                 'name': 'guid',
42045                                 'type': 'int'
42046                             }
42047                         ]
42048                     }
42049                 },
42050                 toolbar : {
42051                     xtype: 'Toolbar',
42052                     xns: Roo,
42053                     items : [
42054                         {
42055                             xtype: 'Button',
42056                             xns: Roo.Toolbar,
42057                             listeners : {
42058                                 click : function (_self, e)
42059                                 {
42060                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42061                                     sd.setMonth(sd.getMonth()-1);
42062                                     _this.monthField.setValue(sd.format('Y-m-d'));
42063                                     _this.grid.ds.load({});
42064                                 }
42065                             },
42066                             text : "Back"
42067                         },
42068                         {
42069                             xtype: 'Separator',
42070                             xns: Roo.Toolbar
42071                         },
42072                         {
42073                             xtype: 'MonthField',
42074                             xns: Roo.form,
42075                             listeners : {
42076                                 render : function (_self)
42077                                 {
42078                                     _this.monthField = _self;
42079                                    // _this.monthField.set  today
42080                                 },
42081                                 select : function (combo, date)
42082                                 {
42083                                     _this.grid.ds.load({});
42084                                 }
42085                             },
42086                             value : (function() { return new Date(); })()
42087                         },
42088                         {
42089                             xtype: 'Separator',
42090                             xns: Roo.Toolbar
42091                         },
42092                         {
42093                             xtype: 'TextItem',
42094                             xns: Roo.Toolbar,
42095                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42096                         },
42097                         {
42098                             xtype: 'Fill',
42099                             xns: Roo.Toolbar
42100                         },
42101                         {
42102                             xtype: 'Button',
42103                             xns: Roo.Toolbar,
42104                             listeners : {
42105                                 click : function (_self, e)
42106                                 {
42107                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42108                                     sd.setMonth(sd.getMonth()+1);
42109                                     _this.monthField.setValue(sd.format('Y-m-d'));
42110                                     _this.grid.ds.load({});
42111                                 }
42112                             },
42113                             text : "Next"
42114                         }
42115                     ]
42116                 },
42117                  
42118             }
42119         };
42120         
42121         *//*
42122  * Based on:
42123  * Ext JS Library 1.1.1
42124  * Copyright(c) 2006-2007, Ext JS, LLC.
42125  *
42126  * Originally Released Under LGPL - original licence link has changed is not relivant.
42127  *
42128  * Fork - LGPL
42129  * <script type="text/javascript">
42130  */
42131  
42132 /**
42133  * @class Roo.LoadMask
42134  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42135  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42136  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42137  * element's UpdateManager load indicator and will be destroyed after the initial load.
42138  * @constructor
42139  * Create a new LoadMask
42140  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42141  * @param {Object} config The config object
42142  */
42143 Roo.LoadMask = function(el, config){
42144     this.el = Roo.get(el);
42145     Roo.apply(this, config);
42146     if(this.store){
42147         this.store.on('beforeload', this.onBeforeLoad, this);
42148         this.store.on('load', this.onLoad, this);
42149         this.store.on('loadexception', this.onLoadException, this);
42150         this.removeMask = false;
42151     }else{
42152         var um = this.el.getUpdateManager();
42153         um.showLoadIndicator = false; // disable the default indicator
42154         um.on('beforeupdate', this.onBeforeLoad, this);
42155         um.on('update', this.onLoad, this);
42156         um.on('failure', this.onLoad, this);
42157         this.removeMask = true;
42158     }
42159 };
42160
42161 Roo.LoadMask.prototype = {
42162     /**
42163      * @cfg {Boolean} removeMask
42164      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42165      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42166      */
42167     /**
42168      * @cfg {String} msg
42169      * The text to display in a centered loading message box (defaults to 'Loading...')
42170      */
42171     msg : 'Loading...',
42172     /**
42173      * @cfg {String} msgCls
42174      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42175      */
42176     msgCls : 'x-mask-loading',
42177
42178     /**
42179      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42180      * @type Boolean
42181      */
42182     disabled: false,
42183
42184     /**
42185      * Disables the mask to prevent it from being displayed
42186      */
42187     disable : function(){
42188        this.disabled = true;
42189     },
42190
42191     /**
42192      * Enables the mask so that it can be displayed
42193      */
42194     enable : function(){
42195         this.disabled = false;
42196     },
42197     
42198     onLoadException : function()
42199     {
42200         Roo.log(arguments);
42201         
42202         if (typeof(arguments[3]) != 'undefined') {
42203             Roo.MessageBox.alert("Error loading",arguments[3]);
42204         } 
42205         /*
42206         try {
42207             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42208                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42209             }   
42210         } catch(e) {
42211             
42212         }
42213         */
42214     
42215         
42216         
42217         this.el.unmask(this.removeMask);
42218     },
42219     // private
42220     onLoad : function()
42221     {
42222         this.el.unmask(this.removeMask);
42223     },
42224
42225     // private
42226     onBeforeLoad : function(){
42227         if(!this.disabled){
42228             this.el.mask(this.msg, this.msgCls);
42229         }
42230     },
42231
42232     // private
42233     destroy : function(){
42234         if(this.store){
42235             this.store.un('beforeload', this.onBeforeLoad, this);
42236             this.store.un('load', this.onLoad, this);
42237             this.store.un('loadexception', this.onLoadException, this);
42238         }else{
42239             var um = this.el.getUpdateManager();
42240             um.un('beforeupdate', this.onBeforeLoad, this);
42241             um.un('update', this.onLoad, this);
42242             um.un('failure', this.onLoad, this);
42243         }
42244     }
42245 };/*
42246  * Based on:
42247  * Ext JS Library 1.1.1
42248  * Copyright(c) 2006-2007, Ext JS, LLC.
42249  *
42250  * Originally Released Under LGPL - original licence link has changed is not relivant.
42251  *
42252  * Fork - LGPL
42253  * <script type="text/javascript">
42254  */
42255
42256
42257 /**
42258  * @class Roo.XTemplate
42259  * @extends Roo.Template
42260  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42261 <pre><code>
42262 var t = new Roo.XTemplate(
42263         '&lt;select name="{name}"&gt;',
42264                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42265         '&lt;/select&gt;'
42266 );
42267  
42268 // then append, applying the master template values
42269  </code></pre>
42270  *
42271  * Supported features:
42272  *
42273  *  Tags:
42274
42275 <pre><code>
42276       {a_variable} - output encoded.
42277       {a_variable.format:("Y-m-d")} - call a method on the variable
42278       {a_variable:raw} - unencoded output
42279       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42280       {a_variable:this.method_on_template(...)} - call a method on the template object.
42281  
42282 </code></pre>
42283  *  The tpl tag:
42284 <pre><code>
42285         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42286         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42287         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42288         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42289   
42290         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42291         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42292 </code></pre>
42293  *      
42294  */
42295 Roo.XTemplate = function()
42296 {
42297     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42298     if (this.html) {
42299         this.compile();
42300     }
42301 };
42302
42303
42304 Roo.extend(Roo.XTemplate, Roo.Template, {
42305
42306     /**
42307      * The various sub templates
42308      */
42309     tpls : false,
42310     /**
42311      *
42312      * basic tag replacing syntax
42313      * WORD:WORD()
42314      *
42315      * // you can fake an object call by doing this
42316      *  x.t:(test,tesT) 
42317      * 
42318      */
42319     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42320
42321     /**
42322      * compile the template
42323      *
42324      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42325      *
42326      */
42327     compile: function()
42328     {
42329         var s = this.html;
42330      
42331         s = ['<tpl>', s, '</tpl>'].join('');
42332     
42333         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42334             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42335             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42336             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42337             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42338             m,
42339             id     = 0,
42340             tpls   = [];
42341     
42342         while(true == !!(m = s.match(re))){
42343             var forMatch   = m[0].match(nameRe),
42344                 ifMatch   = m[0].match(ifRe),
42345                 execMatch   = m[0].match(execRe),
42346                 namedMatch   = m[0].match(namedRe),
42347                 
42348                 exp  = null, 
42349                 fn   = null,
42350                 exec = null,
42351                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42352                 
42353             if (ifMatch) {
42354                 // if - puts fn into test..
42355                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42356                 if(exp){
42357                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42358                 }
42359             }
42360             
42361             if (execMatch) {
42362                 // exec - calls a function... returns empty if true is  returned.
42363                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42364                 if(exp){
42365                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42366                 }
42367             }
42368             
42369             
42370             if (name) {
42371                 // for = 
42372                 switch(name){
42373                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42374                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42375                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42376                 }
42377             }
42378             var uid = namedMatch ? namedMatch[1] : id;
42379             
42380             
42381             tpls.push({
42382                 id:     namedMatch ? namedMatch[1] : id,
42383                 target: name,
42384                 exec:   exec,
42385                 test:   fn,
42386                 body:   m[1] || ''
42387             });
42388             if (namedMatch) {
42389                 s = s.replace(m[0], '');
42390             } else { 
42391                 s = s.replace(m[0], '{xtpl'+ id + '}');
42392             }
42393             ++id;
42394         }
42395         this.tpls = [];
42396         for(var i = tpls.length-1; i >= 0; --i){
42397             this.compileTpl(tpls[i]);
42398             this.tpls[tpls[i].id] = tpls[i];
42399         }
42400         this.master = tpls[tpls.length-1];
42401         return this;
42402     },
42403     /**
42404      * same as applyTemplate, except it's done to one of the subTemplates
42405      * when using named templates, you can do:
42406      *
42407      * var str = pl.applySubTemplate('your-name', values);
42408      *
42409      * 
42410      * @param {Number} id of the template
42411      * @param {Object} values to apply to template
42412      * @param {Object} parent (normaly the instance of this object)
42413      */
42414     applySubTemplate : function(id, values, parent)
42415     {
42416         
42417         
42418         var t = this.tpls[id];
42419         
42420         
42421         try { 
42422             if(t.test && !t.test.call(this, values, parent)){
42423                 return '';
42424             }
42425         } catch(e) {
42426             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42427             Roo.log(e.toString());
42428             Roo.log(t.test);
42429             return ''
42430         }
42431         try { 
42432             
42433             if(t.exec && t.exec.call(this, values, parent)){
42434                 return '';
42435             }
42436         } catch(e) {
42437             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42438             Roo.log(e.toString());
42439             Roo.log(t.exec);
42440             return ''
42441         }
42442         try {
42443             var vs = t.target ? t.target.call(this, values, parent) : values;
42444             parent = t.target ? values : parent;
42445             if(t.target && vs instanceof Array){
42446                 var buf = [];
42447                 for(var i = 0, len = vs.length; i < len; i++){
42448                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42449                 }
42450                 return buf.join('');
42451             }
42452             return t.compiled.call(this, vs, parent);
42453         } catch (e) {
42454             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42455             Roo.log(e.toString());
42456             Roo.log(t.compiled);
42457             return '';
42458         }
42459     },
42460
42461     compileTpl : function(tpl)
42462     {
42463         var fm = Roo.util.Format;
42464         var useF = this.disableFormats !== true;
42465         var sep = Roo.isGecko ? "+" : ",";
42466         var undef = function(str) {
42467             Roo.log("Property not found :"  + str);
42468             return '';
42469         };
42470         
42471         var fn = function(m, name, format, args)
42472         {
42473             //Roo.log(arguments);
42474             args = args ? args.replace(/\\'/g,"'") : args;
42475             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42476             if (typeof(format) == 'undefined') {
42477                 format= 'htmlEncode';
42478             }
42479             if (format == 'raw' ) {
42480                 format = false;
42481             }
42482             
42483             if(name.substr(0, 4) == 'xtpl'){
42484                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42485             }
42486             
42487             // build an array of options to determine if value is undefined..
42488             
42489             // basically get 'xxxx.yyyy' then do
42490             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42491             //    (function () { Roo.log("Property not found"); return ''; })() :
42492             //    ......
42493             
42494             var udef_ar = [];
42495             var lookfor = '';
42496             Roo.each(name.split('.'), function(st) {
42497                 lookfor += (lookfor.length ? '.': '') + st;
42498                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42499             });
42500             
42501             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42502             
42503             
42504             if(format && useF){
42505                 
42506                 args = args ? ',' + args : "";
42507                  
42508                 if(format.substr(0, 5) != "this."){
42509                     format = "fm." + format + '(';
42510                 }else{
42511                     format = 'this.call("'+ format.substr(5) + '", ';
42512                     args = ", values";
42513                 }
42514                 
42515                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42516             }
42517              
42518             if (args.length) {
42519                 // called with xxyx.yuu:(test,test)
42520                 // change to ()
42521                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42522             }
42523             // raw.. - :raw modifier..
42524             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42525             
42526         };
42527         var body;
42528         // branched to use + in gecko and [].join() in others
42529         if(Roo.isGecko){
42530             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42531                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42532                     "';};};";
42533         }else{
42534             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42535             body.push(tpl.body.replace(/(\r\n|\n)/g,
42536                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42537             body.push("'].join('');};};");
42538             body = body.join('');
42539         }
42540         
42541         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42542        
42543         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42544         eval(body);
42545         
42546         return this;
42547     },
42548
42549     applyTemplate : function(values){
42550         return this.master.compiled.call(this, values, {});
42551         //var s = this.subs;
42552     },
42553
42554     apply : function(){
42555         return this.applyTemplate.apply(this, arguments);
42556     }
42557
42558  });
42559
42560 Roo.XTemplate.from = function(el){
42561     el = Roo.getDom(el);
42562     return new Roo.XTemplate(el.value || el.innerHTML);
42563 };