31e2a847461d496a752e3db94a6475451cf93a58
[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) {
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 });/*
24643  * Based on:
24644  * Ext JS Library 1.1.1
24645  * Copyright(c) 2006-2007, Ext JS, LLC.
24646  *
24647  * Originally Released Under LGPL - original licence link has changed is not relivant.
24648  *
24649  * Fork - LGPL
24650  * <script type="text/javascript">
24651  */
24652  
24653 /**
24654  * @class Roo.form.Radio
24655  * @extends Roo.form.Checkbox
24656  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24657  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24658  * @constructor
24659  * Creates a new Radio
24660  * @param {Object} config Configuration options
24661  */
24662 Roo.form.Radio = function(){
24663     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24664 };
24665 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24666     inputType: 'radio',
24667
24668     /**
24669      * If this radio is part of a group, it will return the selected value
24670      * @return {String}
24671      */
24672     getGroupValue : function(){
24673         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24674     },
24675     
24676     
24677     onRender : function(ct, position){
24678         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24679         
24680         if(this.inputValue !== undefined){
24681             this.el.dom.value = this.inputValue;
24682         }
24683          
24684         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24685         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24686         //var viewEl = this.wrap.createChild({ 
24687         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24688         //this.viewEl = viewEl;   
24689         //this.wrap.on('click', this.onClick,  this); 
24690         
24691         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24692         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24693         
24694         
24695         
24696         if(this.boxLabel){
24697             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24698         //    viewEl.on('click', this.onClick,  this); 
24699         }
24700          if(this.checked){
24701             this.el.dom.checked =   'checked' ;
24702         }
24703          
24704     } 
24705     
24706     
24707 });//<script type="text/javascript">
24708
24709 /*
24710  * Based  Ext JS Library 1.1.1
24711  * Copyright(c) 2006-2007, Ext JS, LLC.
24712  * LGPL
24713  *
24714  */
24715  
24716 /**
24717  * @class Roo.HtmlEditorCore
24718  * @extends Roo.Component
24719  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24720  *
24721  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24722  */
24723
24724 Roo.HtmlEditorCore = function(config){
24725     
24726     
24727     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24728     
24729     
24730     this.addEvents({
24731         /**
24732          * @event initialize
24733          * Fires when the editor is fully initialized (including the iframe)
24734          * @param {Roo.HtmlEditorCore} this
24735          */
24736         initialize: true,
24737         /**
24738          * @event activate
24739          * Fires when the editor is first receives the focus. Any insertion must wait
24740          * until after this event.
24741          * @param {Roo.HtmlEditorCore} this
24742          */
24743         activate: true,
24744          /**
24745          * @event beforesync
24746          * Fires before the textarea is updated with content from the editor iframe. Return false
24747          * to cancel the sync.
24748          * @param {Roo.HtmlEditorCore} this
24749          * @param {String} html
24750          */
24751         beforesync: true,
24752          /**
24753          * @event beforepush
24754          * Fires before the iframe editor is updated with content from the textarea. Return false
24755          * to cancel the push.
24756          * @param {Roo.HtmlEditorCore} this
24757          * @param {String} html
24758          */
24759         beforepush: true,
24760          /**
24761          * @event sync
24762          * Fires when the textarea is updated with content from the editor iframe.
24763          * @param {Roo.HtmlEditorCore} this
24764          * @param {String} html
24765          */
24766         sync: true,
24767          /**
24768          * @event push
24769          * Fires when the iframe editor is updated with content from the textarea.
24770          * @param {Roo.HtmlEditorCore} this
24771          * @param {String} html
24772          */
24773         push: true,
24774         
24775         /**
24776          * @event editorevent
24777          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24778          * @param {Roo.HtmlEditorCore} this
24779          */
24780         editorevent: true
24781         
24782     });
24783     
24784     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24785     
24786     // defaults : white / black...
24787     this.applyBlacklists();
24788     
24789     
24790     
24791 };
24792
24793
24794 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24795
24796
24797      /**
24798      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24799      */
24800     
24801     owner : false,
24802     
24803      /**
24804      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24805      *                        Roo.resizable.
24806      */
24807     resizable : false,
24808      /**
24809      * @cfg {Number} height (in pixels)
24810      */   
24811     height: 300,
24812    /**
24813      * @cfg {Number} width (in pixels)
24814      */   
24815     width: 500,
24816     
24817     /**
24818      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24819      * 
24820      */
24821     stylesheets: false,
24822     
24823     // id of frame..
24824     frameId: false,
24825     
24826     // private properties
24827     validationEvent : false,
24828     deferHeight: true,
24829     initialized : false,
24830     activated : false,
24831     sourceEditMode : false,
24832     onFocus : Roo.emptyFn,
24833     iframePad:3,
24834     hideMode:'offsets',
24835     
24836     clearUp: true,
24837     
24838     // blacklist + whitelisted elements..
24839     black: false,
24840     white: false,
24841      
24842     
24843
24844     /**
24845      * Protected method that will not generally be called directly. It
24846      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24847      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24848      */
24849     getDocMarkup : function(){
24850         // body styles..
24851         var st = '';
24852         
24853         // inherit styels from page...?? 
24854         if (this.stylesheets === false) {
24855             
24856             Roo.get(document.head).select('style').each(function(node) {
24857                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24858             });
24859             
24860             Roo.get(document.head).select('link').each(function(node) { 
24861                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24862             });
24863             
24864         } else if (!this.stylesheets.length) {
24865                 // simple..
24866                 st = '<style type="text/css">' +
24867                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24868                    '</style>';
24869         } else { 
24870             
24871         }
24872         
24873         st +=  '<style type="text/css">' +
24874             'IMG { cursor: pointer } ' +
24875         '</style>';
24876
24877         
24878         return '<html><head>' + st  +
24879             //<style type="text/css">' +
24880             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24881             //'</style>' +
24882             ' </head><body class="roo-htmleditor-body"></body></html>';
24883     },
24884
24885     // private
24886     onRender : function(ct, position)
24887     {
24888         var _t = this;
24889         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24890         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24891         
24892         
24893         this.el.dom.style.border = '0 none';
24894         this.el.dom.setAttribute('tabIndex', -1);
24895         this.el.addClass('x-hidden hide');
24896         
24897         
24898         
24899         if(Roo.isIE){ // fix IE 1px bogus margin
24900             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24901         }
24902        
24903         
24904         this.frameId = Roo.id();
24905         
24906          
24907         
24908         var iframe = this.owner.wrap.createChild({
24909             tag: 'iframe',
24910             cls: 'form-control', // bootstrap..
24911             id: this.frameId,
24912             name: this.frameId,
24913             frameBorder : 'no',
24914             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24915         }, this.el
24916         );
24917         
24918         
24919         this.iframe = iframe.dom;
24920
24921          this.assignDocWin();
24922         
24923         this.doc.designMode = 'on';
24924        
24925         this.doc.open();
24926         this.doc.write(this.getDocMarkup());
24927         this.doc.close();
24928
24929         
24930         var task = { // must defer to wait for browser to be ready
24931             run : function(){
24932                 //console.log("run task?" + this.doc.readyState);
24933                 this.assignDocWin();
24934                 if(this.doc.body || this.doc.readyState == 'complete'){
24935                     try {
24936                         this.doc.designMode="on";
24937                     } catch (e) {
24938                         return;
24939                     }
24940                     Roo.TaskMgr.stop(task);
24941                     this.initEditor.defer(10, this);
24942                 }
24943             },
24944             interval : 10,
24945             duration: 10000,
24946             scope: this
24947         };
24948         Roo.TaskMgr.start(task);
24949
24950     },
24951
24952     // private
24953     onResize : function(w, h)
24954     {
24955          Roo.log('resize: ' +w + ',' + h );
24956         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24957         if(!this.iframe){
24958             return;
24959         }
24960         if(typeof w == 'number'){
24961             
24962             this.iframe.style.width = w + 'px';
24963         }
24964         if(typeof h == 'number'){
24965             
24966             this.iframe.style.height = h + 'px';
24967             if(this.doc){
24968                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24969             }
24970         }
24971         
24972     },
24973
24974     /**
24975      * Toggles the editor between standard and source edit mode.
24976      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24977      */
24978     toggleSourceEdit : function(sourceEditMode){
24979         
24980         this.sourceEditMode = sourceEditMode === true;
24981         
24982         if(this.sourceEditMode){
24983  
24984             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24985             
24986         }else{
24987             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24988             //this.iframe.className = '';
24989             this.deferFocus();
24990         }
24991         //this.setSize(this.owner.wrap.getSize());
24992         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24993     },
24994
24995     
24996   
24997
24998     /**
24999      * Protected method that will not generally be called directly. If you need/want
25000      * custom HTML cleanup, this is the method you should override.
25001      * @param {String} html The HTML to be cleaned
25002      * return {String} The cleaned HTML
25003      */
25004     cleanHtml : function(html){
25005         html = String(html);
25006         if(html.length > 5){
25007             if(Roo.isSafari){ // strip safari nonsense
25008                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25009             }
25010         }
25011         if(html == '&nbsp;'){
25012             html = '';
25013         }
25014         return html;
25015     },
25016
25017     /**
25018      * HTML Editor -> Textarea
25019      * Protected method that will not generally be called directly. Syncs the contents
25020      * of the editor iframe with the textarea.
25021      */
25022     syncValue : function(){
25023         if(this.initialized){
25024             var bd = (this.doc.body || this.doc.documentElement);
25025             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25026             var html = bd.innerHTML;
25027             if(Roo.isSafari){
25028                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25029                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25030                 if(m && m[1]){
25031                     html = '<div style="'+m[0]+'">' + html + '</div>';
25032                 }
25033             }
25034             html = this.cleanHtml(html);
25035             // fix up the special chars.. normaly like back quotes in word...
25036             // however we do not want to do this with chinese..
25037             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25038                 var cc = b.charCodeAt();
25039                 if (
25040                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25041                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25042                     (cc >= 0xf900 && cc < 0xfb00 )
25043                 ) {
25044                         return b;
25045                 }
25046                 return "&#"+cc+";" 
25047             });
25048             if(this.owner.fireEvent('beforesync', this, html) !== false){
25049                 this.el.dom.value = html;
25050                 this.owner.fireEvent('sync', this, html);
25051             }
25052         }
25053     },
25054
25055     /**
25056      * Protected method that will not generally be called directly. Pushes the value of the textarea
25057      * into the iframe editor.
25058      */
25059     pushValue : function(){
25060         if(this.initialized){
25061             var v = this.el.dom.value.trim();
25062             
25063 //            if(v.length < 1){
25064 //                v = '&#160;';
25065 //            }
25066             
25067             if(this.owner.fireEvent('beforepush', this, v) !== false){
25068                 var d = (this.doc.body || this.doc.documentElement);
25069                 d.innerHTML = v;
25070                 this.cleanUpPaste();
25071                 this.el.dom.value = d.innerHTML;
25072                 this.owner.fireEvent('push', this, v);
25073             }
25074         }
25075     },
25076
25077     // private
25078     deferFocus : function(){
25079         this.focus.defer(10, this);
25080     },
25081
25082     // doc'ed in Field
25083     focus : function(){
25084         if(this.win && !this.sourceEditMode){
25085             this.win.focus();
25086         }else{
25087             this.el.focus();
25088         }
25089     },
25090     
25091     assignDocWin: function()
25092     {
25093         var iframe = this.iframe;
25094         
25095          if(Roo.isIE){
25096             this.doc = iframe.contentWindow.document;
25097             this.win = iframe.contentWindow;
25098         } else {
25099 //            if (!Roo.get(this.frameId)) {
25100 //                return;
25101 //            }
25102 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25103 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25104             
25105             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25106                 return;
25107             }
25108             
25109             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25110             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25111         }
25112     },
25113     
25114     // private
25115     initEditor : function(){
25116         //console.log("INIT EDITOR");
25117         this.assignDocWin();
25118         
25119         
25120         
25121         this.doc.designMode="on";
25122         this.doc.open();
25123         this.doc.write(this.getDocMarkup());
25124         this.doc.close();
25125         
25126         var dbody = (this.doc.body || this.doc.documentElement);
25127         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25128         // this copies styles from the containing element into thsi one..
25129         // not sure why we need all of this..
25130         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25131         
25132         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25133         //ss['background-attachment'] = 'fixed'; // w3c
25134         dbody.bgProperties = 'fixed'; // ie
25135         //Roo.DomHelper.applyStyles(dbody, ss);
25136         Roo.EventManager.on(this.doc, {
25137             //'mousedown': this.onEditorEvent,
25138             'mouseup': this.onEditorEvent,
25139             'dblclick': this.onEditorEvent,
25140             'click': this.onEditorEvent,
25141             'keyup': this.onEditorEvent,
25142             buffer:100,
25143             scope: this
25144         });
25145         if(Roo.isGecko){
25146             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25147         }
25148         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25149             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25150         }
25151         this.initialized = true;
25152
25153         this.owner.fireEvent('initialize', this);
25154         this.pushValue();
25155     },
25156
25157     // private
25158     onDestroy : function(){
25159         
25160         
25161         
25162         if(this.rendered){
25163             
25164             //for (var i =0; i < this.toolbars.length;i++) {
25165             //    // fixme - ask toolbars for heights?
25166             //    this.toolbars[i].onDestroy();
25167            // }
25168             
25169             //this.wrap.dom.innerHTML = '';
25170             //this.wrap.remove();
25171         }
25172     },
25173
25174     // private
25175     onFirstFocus : function(){
25176         
25177         this.assignDocWin();
25178         
25179         
25180         this.activated = true;
25181          
25182     
25183         if(Roo.isGecko){ // prevent silly gecko errors
25184             this.win.focus();
25185             var s = this.win.getSelection();
25186             if(!s.focusNode || s.focusNode.nodeType != 3){
25187                 var r = s.getRangeAt(0);
25188                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25189                 r.collapse(true);
25190                 this.deferFocus();
25191             }
25192             try{
25193                 this.execCmd('useCSS', true);
25194                 this.execCmd('styleWithCSS', false);
25195             }catch(e){}
25196         }
25197         this.owner.fireEvent('activate', this);
25198     },
25199
25200     // private
25201     adjustFont: function(btn){
25202         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25203         //if(Roo.isSafari){ // safari
25204         //    adjust *= 2;
25205        // }
25206         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25207         if(Roo.isSafari){ // safari
25208             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25209             v =  (v < 10) ? 10 : v;
25210             v =  (v > 48) ? 48 : v;
25211             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25212             
25213         }
25214         
25215         
25216         v = Math.max(1, v+adjust);
25217         
25218         this.execCmd('FontSize', v  );
25219     },
25220
25221     onEditorEvent : function(e)
25222     {
25223         this.owner.fireEvent('editorevent', this, e);
25224       //  this.updateToolbar();
25225         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25226     },
25227
25228     insertTag : function(tg)
25229     {
25230         // could be a bit smarter... -> wrap the current selected tRoo..
25231         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25232             
25233             range = this.createRange(this.getSelection());
25234             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25235             wrappingNode.appendChild(range.extractContents());
25236             range.insertNode(wrappingNode);
25237
25238             return;
25239             
25240             
25241             
25242         }
25243         this.execCmd("formatblock",   tg);
25244         
25245     },
25246     
25247     insertText : function(txt)
25248     {
25249         
25250         
25251         var range = this.createRange();
25252         range.deleteContents();
25253                //alert(Sender.getAttribute('label'));
25254                
25255         range.insertNode(this.doc.createTextNode(txt));
25256     } ,
25257     
25258      
25259
25260     /**
25261      * Executes a Midas editor command on the editor document and performs necessary focus and
25262      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25263      * @param {String} cmd The Midas command
25264      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25265      */
25266     relayCmd : function(cmd, value){
25267         this.win.focus();
25268         this.execCmd(cmd, value);
25269         this.owner.fireEvent('editorevent', this);
25270         //this.updateToolbar();
25271         this.owner.deferFocus();
25272     },
25273
25274     /**
25275      * Executes a Midas editor command directly on the editor document.
25276      * For visual commands, you should use {@link #relayCmd} instead.
25277      * <b>This should only be called after the editor is initialized.</b>
25278      * @param {String} cmd The Midas command
25279      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25280      */
25281     execCmd : function(cmd, value){
25282         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25283         this.syncValue();
25284     },
25285  
25286  
25287    
25288     /**
25289      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25290      * to insert tRoo.
25291      * @param {String} text | dom node.. 
25292      */
25293     insertAtCursor : function(text)
25294     {
25295         
25296         
25297         
25298         if(!this.activated){
25299             return;
25300         }
25301         /*
25302         if(Roo.isIE){
25303             this.win.focus();
25304             var r = this.doc.selection.createRange();
25305             if(r){
25306                 r.collapse(true);
25307                 r.pasteHTML(text);
25308                 this.syncValue();
25309                 this.deferFocus();
25310             
25311             }
25312             return;
25313         }
25314         */
25315         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25316             this.win.focus();
25317             
25318             
25319             // from jquery ui (MIT licenced)
25320             var range, node;
25321             var win = this.win;
25322             
25323             if (win.getSelection && win.getSelection().getRangeAt) {
25324                 range = win.getSelection().getRangeAt(0);
25325                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25326                 range.insertNode(node);
25327             } else if (win.document.selection && win.document.selection.createRange) {
25328                 // no firefox support
25329                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25330                 win.document.selection.createRange().pasteHTML(txt);
25331             } else {
25332                 // no firefox support
25333                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25334                 this.execCmd('InsertHTML', txt);
25335             } 
25336             
25337             this.syncValue();
25338             
25339             this.deferFocus();
25340         }
25341     },
25342  // private
25343     mozKeyPress : function(e){
25344         if(e.ctrlKey){
25345             var c = e.getCharCode(), cmd;
25346           
25347             if(c > 0){
25348                 c = String.fromCharCode(c).toLowerCase();
25349                 switch(c){
25350                     case 'b':
25351                         cmd = 'bold';
25352                         break;
25353                     case 'i':
25354                         cmd = 'italic';
25355                         break;
25356                     
25357                     case 'u':
25358                         cmd = 'underline';
25359                         break;
25360                     
25361                     case 'v':
25362                         this.cleanUpPaste.defer(100, this);
25363                         return;
25364                         
25365                 }
25366                 if(cmd){
25367                     this.win.focus();
25368                     this.execCmd(cmd);
25369                     this.deferFocus();
25370                     e.preventDefault();
25371                 }
25372                 
25373             }
25374         }
25375     },
25376
25377     // private
25378     fixKeys : function(){ // load time branching for fastest keydown performance
25379         if(Roo.isIE){
25380             return function(e){
25381                 var k = e.getKey(), r;
25382                 if(k == e.TAB){
25383                     e.stopEvent();
25384                     r = this.doc.selection.createRange();
25385                     if(r){
25386                         r.collapse(true);
25387                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25388                         this.deferFocus();
25389                     }
25390                     return;
25391                 }
25392                 
25393                 if(k == e.ENTER){
25394                     r = this.doc.selection.createRange();
25395                     if(r){
25396                         var target = r.parentElement();
25397                         if(!target || target.tagName.toLowerCase() != 'li'){
25398                             e.stopEvent();
25399                             r.pasteHTML('<br />');
25400                             r.collapse(false);
25401                             r.select();
25402                         }
25403                     }
25404                 }
25405                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25406                     this.cleanUpPaste.defer(100, this);
25407                     return;
25408                 }
25409                 
25410                 
25411             };
25412         }else if(Roo.isOpera){
25413             return function(e){
25414                 var k = e.getKey();
25415                 if(k == e.TAB){
25416                     e.stopEvent();
25417                     this.win.focus();
25418                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25419                     this.deferFocus();
25420                 }
25421                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25422                     this.cleanUpPaste.defer(100, this);
25423                     return;
25424                 }
25425                 
25426             };
25427         }else if(Roo.isSafari){
25428             return function(e){
25429                 var k = e.getKey();
25430                 
25431                 if(k == e.TAB){
25432                     e.stopEvent();
25433                     this.execCmd('InsertText','\t');
25434                     this.deferFocus();
25435                     return;
25436                 }
25437                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25438                     this.cleanUpPaste.defer(100, this);
25439                     return;
25440                 }
25441                 
25442              };
25443         }
25444     }(),
25445     
25446     getAllAncestors: function()
25447     {
25448         var p = this.getSelectedNode();
25449         var a = [];
25450         if (!p) {
25451             a.push(p); // push blank onto stack..
25452             p = this.getParentElement();
25453         }
25454         
25455         
25456         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25457             a.push(p);
25458             p = p.parentNode;
25459         }
25460         a.push(this.doc.body);
25461         return a;
25462     },
25463     lastSel : false,
25464     lastSelNode : false,
25465     
25466     
25467     getSelection : function() 
25468     {
25469         this.assignDocWin();
25470         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25471     },
25472     
25473     getSelectedNode: function() 
25474     {
25475         // this may only work on Gecko!!!
25476         
25477         // should we cache this!!!!
25478         
25479         
25480         
25481          
25482         var range = this.createRange(this.getSelection()).cloneRange();
25483         
25484         if (Roo.isIE) {
25485             var parent = range.parentElement();
25486             while (true) {
25487                 var testRange = range.duplicate();
25488                 testRange.moveToElementText(parent);
25489                 if (testRange.inRange(range)) {
25490                     break;
25491                 }
25492                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25493                     break;
25494                 }
25495                 parent = parent.parentElement;
25496             }
25497             return parent;
25498         }
25499         
25500         // is ancestor a text element.
25501         var ac =  range.commonAncestorContainer;
25502         if (ac.nodeType == 3) {
25503             ac = ac.parentNode;
25504         }
25505         
25506         var ar = ac.childNodes;
25507          
25508         var nodes = [];
25509         var other_nodes = [];
25510         var has_other_nodes = false;
25511         for (var i=0;i<ar.length;i++) {
25512             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25513                 continue;
25514             }
25515             // fullly contained node.
25516             
25517             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25518                 nodes.push(ar[i]);
25519                 continue;
25520             }
25521             
25522             // probably selected..
25523             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25524                 other_nodes.push(ar[i]);
25525                 continue;
25526             }
25527             // outer..
25528             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25529                 continue;
25530             }
25531             
25532             
25533             has_other_nodes = true;
25534         }
25535         if (!nodes.length && other_nodes.length) {
25536             nodes= other_nodes;
25537         }
25538         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25539             return false;
25540         }
25541         
25542         return nodes[0];
25543     },
25544     createRange: function(sel)
25545     {
25546         // this has strange effects when using with 
25547         // top toolbar - not sure if it's a great idea.
25548         //this.editor.contentWindow.focus();
25549         if (typeof sel != "undefined") {
25550             try {
25551                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25552             } catch(e) {
25553                 return this.doc.createRange();
25554             }
25555         } else {
25556             return this.doc.createRange();
25557         }
25558     },
25559     getParentElement: function()
25560     {
25561         
25562         this.assignDocWin();
25563         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25564         
25565         var range = this.createRange(sel);
25566          
25567         try {
25568             var p = range.commonAncestorContainer;
25569             while (p.nodeType == 3) { // text node
25570                 p = p.parentNode;
25571             }
25572             return p;
25573         } catch (e) {
25574             return null;
25575         }
25576     
25577     },
25578     /***
25579      *
25580      * Range intersection.. the hard stuff...
25581      *  '-1' = before
25582      *  '0' = hits..
25583      *  '1' = after.
25584      *         [ -- selected range --- ]
25585      *   [fail]                        [fail]
25586      *
25587      *    basically..
25588      *      if end is before start or  hits it. fail.
25589      *      if start is after end or hits it fail.
25590      *
25591      *   if either hits (but other is outside. - then it's not 
25592      *   
25593      *    
25594      **/
25595     
25596     
25597     // @see http://www.thismuchiknow.co.uk/?p=64.
25598     rangeIntersectsNode : function(range, node)
25599     {
25600         var nodeRange = node.ownerDocument.createRange();
25601         try {
25602             nodeRange.selectNode(node);
25603         } catch (e) {
25604             nodeRange.selectNodeContents(node);
25605         }
25606     
25607         var rangeStartRange = range.cloneRange();
25608         rangeStartRange.collapse(true);
25609     
25610         var rangeEndRange = range.cloneRange();
25611         rangeEndRange.collapse(false);
25612     
25613         var nodeStartRange = nodeRange.cloneRange();
25614         nodeStartRange.collapse(true);
25615     
25616         var nodeEndRange = nodeRange.cloneRange();
25617         nodeEndRange.collapse(false);
25618     
25619         return rangeStartRange.compareBoundaryPoints(
25620                  Range.START_TO_START, nodeEndRange) == -1 &&
25621                rangeEndRange.compareBoundaryPoints(
25622                  Range.START_TO_START, nodeStartRange) == 1;
25623         
25624          
25625     },
25626     rangeCompareNode : function(range, node)
25627     {
25628         var nodeRange = node.ownerDocument.createRange();
25629         try {
25630             nodeRange.selectNode(node);
25631         } catch (e) {
25632             nodeRange.selectNodeContents(node);
25633         }
25634         
25635         
25636         range.collapse(true);
25637     
25638         nodeRange.collapse(true);
25639      
25640         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25641         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25642          
25643         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25644         
25645         var nodeIsBefore   =  ss == 1;
25646         var nodeIsAfter    = ee == -1;
25647         
25648         if (nodeIsBefore && nodeIsAfter) {
25649             return 0; // outer
25650         }
25651         if (!nodeIsBefore && nodeIsAfter) {
25652             return 1; //right trailed.
25653         }
25654         
25655         if (nodeIsBefore && !nodeIsAfter) {
25656             return 2;  // left trailed.
25657         }
25658         // fully contined.
25659         return 3;
25660     },
25661
25662     // private? - in a new class?
25663     cleanUpPaste :  function()
25664     {
25665         // cleans up the whole document..
25666         Roo.log('cleanuppaste');
25667         
25668         this.cleanUpChildren(this.doc.body);
25669         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25670         if (clean != this.doc.body.innerHTML) {
25671             this.doc.body.innerHTML = clean;
25672         }
25673         
25674     },
25675     
25676     cleanWordChars : function(input) {// change the chars to hex code
25677         var he = Roo.HtmlEditorCore;
25678         
25679         var output = input;
25680         Roo.each(he.swapCodes, function(sw) { 
25681             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25682             
25683             output = output.replace(swapper, sw[1]);
25684         });
25685         
25686         return output;
25687     },
25688     
25689     
25690     cleanUpChildren : function (n)
25691     {
25692         if (!n.childNodes.length) {
25693             return;
25694         }
25695         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25696            this.cleanUpChild(n.childNodes[i]);
25697         }
25698     },
25699     
25700     
25701         
25702     
25703     cleanUpChild : function (node)
25704     {
25705         var ed = this;
25706         //console.log(node);
25707         if (node.nodeName == "#text") {
25708             // clean up silly Windows -- stuff?
25709             return; 
25710         }
25711         if (node.nodeName == "#comment") {
25712             node.parentNode.removeChild(node);
25713             // clean up silly Windows -- stuff?
25714             return; 
25715         }
25716         var lcname = node.tagName.toLowerCase();
25717         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25718         // whitelist of tags..
25719         
25720         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25721             // remove node.
25722             node.parentNode.removeChild(node);
25723             return;
25724             
25725         }
25726         
25727         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25728         
25729         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25730         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25731         
25732         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25733         //    remove_keep_children = true;
25734         //}
25735         
25736         if (remove_keep_children) {
25737             this.cleanUpChildren(node);
25738             // inserts everything just before this node...
25739             while (node.childNodes.length) {
25740                 var cn = node.childNodes[0];
25741                 node.removeChild(cn);
25742                 node.parentNode.insertBefore(cn, node);
25743             }
25744             node.parentNode.removeChild(node);
25745             return;
25746         }
25747         
25748         if (!node.attributes || !node.attributes.length) {
25749             this.cleanUpChildren(node);
25750             return;
25751         }
25752         
25753         function cleanAttr(n,v)
25754         {
25755             
25756             if (v.match(/^\./) || v.match(/^\//)) {
25757                 return;
25758             }
25759             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25760                 return;
25761             }
25762             if (v.match(/^#/)) {
25763                 return;
25764             }
25765 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25766             node.removeAttribute(n);
25767             
25768         }
25769         
25770         var cwhite = this.cwhite;
25771         var cblack = this.cblack;
25772             
25773         function cleanStyle(n,v)
25774         {
25775             if (v.match(/expression/)) { //XSS?? should we even bother..
25776                 node.removeAttribute(n);
25777                 return;
25778             }
25779             
25780             var parts = v.split(/;/);
25781             var clean = [];
25782             
25783             Roo.each(parts, function(p) {
25784                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25785                 if (!p.length) {
25786                     return true;
25787                 }
25788                 var l = p.split(':').shift().replace(/\s+/g,'');
25789                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25790                 
25791                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25792 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25793                     //node.removeAttribute(n);
25794                     return true;
25795                 }
25796                 //Roo.log()
25797                 // only allow 'c whitelisted system attributes'
25798                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25799 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25800                     //node.removeAttribute(n);
25801                     return true;
25802                 }
25803                 
25804                 
25805                  
25806                 
25807                 clean.push(p);
25808                 return true;
25809             });
25810             if (clean.length) { 
25811                 node.setAttribute(n, clean.join(';'));
25812             } else {
25813                 node.removeAttribute(n);
25814             }
25815             
25816         }
25817         
25818         
25819         for (var i = node.attributes.length-1; i > -1 ; i--) {
25820             var a = node.attributes[i];
25821             //console.log(a);
25822             
25823             if (a.name.toLowerCase().substr(0,2)=='on')  {
25824                 node.removeAttribute(a.name);
25825                 continue;
25826             }
25827             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25828                 node.removeAttribute(a.name);
25829                 continue;
25830             }
25831             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25832                 cleanAttr(a.name,a.value); // fixme..
25833                 continue;
25834             }
25835             if (a.name == 'style') {
25836                 cleanStyle(a.name,a.value);
25837                 continue;
25838             }
25839             /// clean up MS crap..
25840             // tecnically this should be a list of valid class'es..
25841             
25842             
25843             if (a.name == 'class') {
25844                 if (a.value.match(/^Mso/)) {
25845                     node.className = '';
25846                 }
25847                 
25848                 if (a.value.match(/body/)) {
25849                     node.className = '';
25850                 }
25851                 continue;
25852             }
25853             
25854             // style cleanup!?
25855             // class cleanup?
25856             
25857         }
25858         
25859         
25860         this.cleanUpChildren(node);
25861         
25862         
25863     },
25864     
25865     /**
25866      * Clean up MS wordisms...
25867      */
25868     cleanWord : function(node)
25869     {
25870         
25871         
25872         if (!node) {
25873             this.cleanWord(this.doc.body);
25874             return;
25875         }
25876         if (node.nodeName == "#text") {
25877             // clean up silly Windows -- stuff?
25878             return; 
25879         }
25880         if (node.nodeName == "#comment") {
25881             node.parentNode.removeChild(node);
25882             // clean up silly Windows -- stuff?
25883             return; 
25884         }
25885         
25886         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25887             node.parentNode.removeChild(node);
25888             return;
25889         }
25890         
25891         // remove - but keep children..
25892         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25893             while (node.childNodes.length) {
25894                 var cn = node.childNodes[0];
25895                 node.removeChild(cn);
25896                 node.parentNode.insertBefore(cn, node);
25897             }
25898             node.parentNode.removeChild(node);
25899             this.iterateChildren(node, this.cleanWord);
25900             return;
25901         }
25902         // clean styles
25903         if (node.className.length) {
25904             
25905             var cn = node.className.split(/\W+/);
25906             var cna = [];
25907             Roo.each(cn, function(cls) {
25908                 if (cls.match(/Mso[a-zA-Z]+/)) {
25909                     return;
25910                 }
25911                 cna.push(cls);
25912             });
25913             node.className = cna.length ? cna.join(' ') : '';
25914             if (!cna.length) {
25915                 node.removeAttribute("class");
25916             }
25917         }
25918         
25919         if (node.hasAttribute("lang")) {
25920             node.removeAttribute("lang");
25921         }
25922         
25923         if (node.hasAttribute("style")) {
25924             
25925             var styles = node.getAttribute("style").split(";");
25926             var nstyle = [];
25927             Roo.each(styles, function(s) {
25928                 if (!s.match(/:/)) {
25929                     return;
25930                 }
25931                 var kv = s.split(":");
25932                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25933                     return;
25934                 }
25935                 // what ever is left... we allow.
25936                 nstyle.push(s);
25937             });
25938             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25939             if (!nstyle.length) {
25940                 node.removeAttribute('style');
25941             }
25942         }
25943         this.iterateChildren(node, this.cleanWord);
25944         
25945         
25946         
25947     },
25948     /**
25949      * iterateChildren of a Node, calling fn each time, using this as the scole..
25950      * @param {DomNode} node node to iterate children of.
25951      * @param {Function} fn method of this class to call on each item.
25952      */
25953     iterateChildren : function(node, fn)
25954     {
25955         if (!node.childNodes.length) {
25956                 return;
25957         }
25958         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25959            fn.call(this, node.childNodes[i])
25960         }
25961     },
25962     
25963     
25964     /**
25965      * cleanTableWidths.
25966      *
25967      * Quite often pasting from word etc.. results in tables with column and widths.
25968      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25969      *
25970      */
25971     cleanTableWidths : function(node)
25972     {
25973          
25974          
25975         if (!node) {
25976             this.cleanTableWidths(this.doc.body);
25977             return;
25978         }
25979         
25980         // ignore list...
25981         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25982             return; 
25983         }
25984         Roo.log(node.tagName);
25985         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25986             this.iterateChildren(node, this.cleanTableWidths);
25987             return;
25988         }
25989         if (node.hasAttribute('width')) {
25990             node.removeAttribute('width');
25991         }
25992         
25993          
25994         if (node.hasAttribute("style")) {
25995             // pretty basic...
25996             
25997             var styles = node.getAttribute("style").split(";");
25998             var nstyle = [];
25999             Roo.each(styles, function(s) {
26000                 if (!s.match(/:/)) {
26001                     return;
26002                 }
26003                 var kv = s.split(":");
26004                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26005                     return;
26006                 }
26007                 // what ever is left... we allow.
26008                 nstyle.push(s);
26009             });
26010             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26011             if (!nstyle.length) {
26012                 node.removeAttribute('style');
26013             }
26014         }
26015         
26016         this.iterateChildren(node, this.cleanTableWidths);
26017         
26018         
26019     },
26020     
26021     
26022     
26023     
26024     domToHTML : function(currentElement, depth, nopadtext) {
26025         
26026         depth = depth || 0;
26027         nopadtext = nopadtext || false;
26028     
26029         if (!currentElement) {
26030             return this.domToHTML(this.doc.body);
26031         }
26032         
26033         //Roo.log(currentElement);
26034         var j;
26035         var allText = false;
26036         var nodeName = currentElement.nodeName;
26037         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26038         
26039         if  (nodeName == '#text') {
26040             
26041             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26042         }
26043         
26044         
26045         var ret = '';
26046         if (nodeName != 'BODY') {
26047              
26048             var i = 0;
26049             // Prints the node tagName, such as <A>, <IMG>, etc
26050             if (tagName) {
26051                 var attr = [];
26052                 for(i = 0; i < currentElement.attributes.length;i++) {
26053                     // quoting?
26054                     var aname = currentElement.attributes.item(i).name;
26055                     if (!currentElement.attributes.item(i).value.length) {
26056                         continue;
26057                     }
26058                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26059                 }
26060                 
26061                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26062             } 
26063             else {
26064                 
26065                 // eack
26066             }
26067         } else {
26068             tagName = false;
26069         }
26070         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26071             return ret;
26072         }
26073         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26074             nopadtext = true;
26075         }
26076         
26077         
26078         // Traverse the tree
26079         i = 0;
26080         var currentElementChild = currentElement.childNodes.item(i);
26081         var allText = true;
26082         var innerHTML  = '';
26083         lastnode = '';
26084         while (currentElementChild) {
26085             // Formatting code (indent the tree so it looks nice on the screen)
26086             var nopad = nopadtext;
26087             if (lastnode == 'SPAN') {
26088                 nopad  = true;
26089             }
26090             // text
26091             if  (currentElementChild.nodeName == '#text') {
26092                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26093                 toadd = nopadtext ? toadd : toadd.trim();
26094                 if (!nopad && toadd.length > 80) {
26095                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26096                 }
26097                 innerHTML  += toadd;
26098                 
26099                 i++;
26100                 currentElementChild = currentElement.childNodes.item(i);
26101                 lastNode = '';
26102                 continue;
26103             }
26104             allText = false;
26105             
26106             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26107                 
26108             // Recursively traverse the tree structure of the child node
26109             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26110             lastnode = currentElementChild.nodeName;
26111             i++;
26112             currentElementChild=currentElement.childNodes.item(i);
26113         }
26114         
26115         ret += innerHTML;
26116         
26117         if (!allText) {
26118                 // The remaining code is mostly for formatting the tree
26119             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26120         }
26121         
26122         
26123         if (tagName) {
26124             ret+= "</"+tagName+">";
26125         }
26126         return ret;
26127         
26128     },
26129         
26130     applyBlacklists : function()
26131     {
26132         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26133         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26134         
26135         this.white = [];
26136         this.black = [];
26137         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26138             if (b.indexOf(tag) > -1) {
26139                 return;
26140             }
26141             this.white.push(tag);
26142             
26143         }, this);
26144         
26145         Roo.each(w, function(tag) {
26146             if (b.indexOf(tag) > -1) {
26147                 return;
26148             }
26149             if (this.white.indexOf(tag) > -1) {
26150                 return;
26151             }
26152             this.white.push(tag);
26153             
26154         }, this);
26155         
26156         
26157         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26158             if (w.indexOf(tag) > -1) {
26159                 return;
26160             }
26161             this.black.push(tag);
26162             
26163         }, this);
26164         
26165         Roo.each(b, function(tag) {
26166             if (w.indexOf(tag) > -1) {
26167                 return;
26168             }
26169             if (this.black.indexOf(tag) > -1) {
26170                 return;
26171             }
26172             this.black.push(tag);
26173             
26174         }, this);
26175         
26176         
26177         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26178         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26179         
26180         this.cwhite = [];
26181         this.cblack = [];
26182         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26183             if (b.indexOf(tag) > -1) {
26184                 return;
26185             }
26186             this.cwhite.push(tag);
26187             
26188         }, this);
26189         
26190         Roo.each(w, function(tag) {
26191             if (b.indexOf(tag) > -1) {
26192                 return;
26193             }
26194             if (this.cwhite.indexOf(tag) > -1) {
26195                 return;
26196             }
26197             this.cwhite.push(tag);
26198             
26199         }, this);
26200         
26201         
26202         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26203             if (w.indexOf(tag) > -1) {
26204                 return;
26205             }
26206             this.cblack.push(tag);
26207             
26208         }, this);
26209         
26210         Roo.each(b, function(tag) {
26211             if (w.indexOf(tag) > -1) {
26212                 return;
26213             }
26214             if (this.cblack.indexOf(tag) > -1) {
26215                 return;
26216             }
26217             this.cblack.push(tag);
26218             
26219         }, this);
26220     },
26221     
26222     setStylesheets : function(stylesheets)
26223     {
26224         if(typeof(stylesheets) == 'string'){
26225             Roo.get(this.iframe.contentDocument.head).createChild({
26226                 tag : 'link',
26227                 rel : 'stylesheet',
26228                 type : 'text/css',
26229                 href : stylesheets
26230             });
26231             
26232             return;
26233         }
26234         var _this = this;
26235      
26236         Roo.each(stylesheets, function(s) {
26237             if(!s.length){
26238                 return;
26239             }
26240             
26241             Roo.get(_this.iframe.contentDocument.head).createChild({
26242                 tag : 'link',
26243                 rel : 'stylesheet',
26244                 type : 'text/css',
26245                 href : s
26246             });
26247         });
26248
26249         
26250     },
26251     
26252     removeStylesheets : function()
26253     {
26254         var _this = this;
26255         
26256         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26257             s.remove();
26258         });
26259     }
26260     
26261     // hide stuff that is not compatible
26262     /**
26263      * @event blur
26264      * @hide
26265      */
26266     /**
26267      * @event change
26268      * @hide
26269      */
26270     /**
26271      * @event focus
26272      * @hide
26273      */
26274     /**
26275      * @event specialkey
26276      * @hide
26277      */
26278     /**
26279      * @cfg {String} fieldClass @hide
26280      */
26281     /**
26282      * @cfg {String} focusClass @hide
26283      */
26284     /**
26285      * @cfg {String} autoCreate @hide
26286      */
26287     /**
26288      * @cfg {String} inputType @hide
26289      */
26290     /**
26291      * @cfg {String} invalidClass @hide
26292      */
26293     /**
26294      * @cfg {String} invalidText @hide
26295      */
26296     /**
26297      * @cfg {String} msgFx @hide
26298      */
26299     /**
26300      * @cfg {String} validateOnBlur @hide
26301      */
26302 });
26303
26304 Roo.HtmlEditorCore.white = [
26305         'area', 'br', 'img', 'input', 'hr', 'wbr',
26306         
26307        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26308        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26309        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26310        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26311        'table',   'ul',         'xmp', 
26312        
26313        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26314       'thead',   'tr', 
26315      
26316       'dir', 'menu', 'ol', 'ul', 'dl',
26317        
26318       'embed',  'object'
26319 ];
26320
26321
26322 Roo.HtmlEditorCore.black = [
26323     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26324         'applet', // 
26325         'base',   'basefont', 'bgsound', 'blink',  'body', 
26326         'frame',  'frameset', 'head',    'html',   'ilayer', 
26327         'iframe', 'layer',  'link',     'meta',    'object',   
26328         'script', 'style' ,'title',  'xml' // clean later..
26329 ];
26330 Roo.HtmlEditorCore.clean = [
26331     'script', 'style', 'title', 'xml'
26332 ];
26333 Roo.HtmlEditorCore.remove = [
26334     'font'
26335 ];
26336 // attributes..
26337
26338 Roo.HtmlEditorCore.ablack = [
26339     'on'
26340 ];
26341     
26342 Roo.HtmlEditorCore.aclean = [ 
26343     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26344 ];
26345
26346 // protocols..
26347 Roo.HtmlEditorCore.pwhite= [
26348         'http',  'https',  'mailto'
26349 ];
26350
26351 // white listed style attributes.
26352 Roo.HtmlEditorCore.cwhite= [
26353       //  'text-align', /// default is to allow most things..
26354       
26355          
26356 //        'font-size'//??
26357 ];
26358
26359 // black listed style attributes.
26360 Roo.HtmlEditorCore.cblack= [
26361       //  'font-size' -- this can be set by the project 
26362 ];
26363
26364
26365 Roo.HtmlEditorCore.swapCodes   =[ 
26366     [    8211, "--" ], 
26367     [    8212, "--" ], 
26368     [    8216,  "'" ],  
26369     [    8217, "'" ],  
26370     [    8220, '"' ],  
26371     [    8221, '"' ],  
26372     [    8226, "*" ],  
26373     [    8230, "..." ]
26374 ]; 
26375
26376     //<script type="text/javascript">
26377
26378 /*
26379  * Ext JS Library 1.1.1
26380  * Copyright(c) 2006-2007, Ext JS, LLC.
26381  * Licence LGPL
26382  * 
26383  */
26384  
26385  
26386 Roo.form.HtmlEditor = function(config){
26387     
26388     
26389     
26390     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26391     
26392     if (!this.toolbars) {
26393         this.toolbars = [];
26394     }
26395     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26396     
26397     
26398 };
26399
26400 /**
26401  * @class Roo.form.HtmlEditor
26402  * @extends Roo.form.Field
26403  * Provides a lightweight HTML Editor component.
26404  *
26405  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26406  * 
26407  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26408  * supported by this editor.</b><br/><br/>
26409  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26410  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26411  */
26412 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26413     /**
26414      * @cfg {Boolean} clearUp
26415      */
26416     clearUp : true,
26417       /**
26418      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26419      */
26420     toolbars : false,
26421    
26422      /**
26423      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26424      *                        Roo.resizable.
26425      */
26426     resizable : false,
26427      /**
26428      * @cfg {Number} height (in pixels)
26429      */   
26430     height: 300,
26431    /**
26432      * @cfg {Number} width (in pixels)
26433      */   
26434     width: 500,
26435     
26436     /**
26437      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26438      * 
26439      */
26440     stylesheets: false,
26441     
26442     
26443      /**
26444      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26445      * 
26446      */
26447     cblack: false,
26448     /**
26449      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26450      * 
26451      */
26452     cwhite: false,
26453     
26454      /**
26455      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26456      * 
26457      */
26458     black: false,
26459     /**
26460      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26461      * 
26462      */
26463     white: false,
26464     
26465     // id of frame..
26466     frameId: false,
26467     
26468     // private properties
26469     validationEvent : false,
26470     deferHeight: true,
26471     initialized : false,
26472     activated : false,
26473     
26474     onFocus : Roo.emptyFn,
26475     iframePad:3,
26476     hideMode:'offsets',
26477     
26478     actionMode : 'container', // defaults to hiding it...
26479     
26480     defaultAutoCreate : { // modified by initCompnoent..
26481         tag: "textarea",
26482         style:"width:500px;height:300px;",
26483         autocomplete: "new-password"
26484     },
26485
26486     // private
26487     initComponent : function(){
26488         this.addEvents({
26489             /**
26490              * @event initialize
26491              * Fires when the editor is fully initialized (including the iframe)
26492              * @param {HtmlEditor} this
26493              */
26494             initialize: true,
26495             /**
26496              * @event activate
26497              * Fires when the editor is first receives the focus. Any insertion must wait
26498              * until after this event.
26499              * @param {HtmlEditor} this
26500              */
26501             activate: true,
26502              /**
26503              * @event beforesync
26504              * Fires before the textarea is updated with content from the editor iframe. Return false
26505              * to cancel the sync.
26506              * @param {HtmlEditor} this
26507              * @param {String} html
26508              */
26509             beforesync: true,
26510              /**
26511              * @event beforepush
26512              * Fires before the iframe editor is updated with content from the textarea. Return false
26513              * to cancel the push.
26514              * @param {HtmlEditor} this
26515              * @param {String} html
26516              */
26517             beforepush: true,
26518              /**
26519              * @event sync
26520              * Fires when the textarea is updated with content from the editor iframe.
26521              * @param {HtmlEditor} this
26522              * @param {String} html
26523              */
26524             sync: true,
26525              /**
26526              * @event push
26527              * Fires when the iframe editor is updated with content from the textarea.
26528              * @param {HtmlEditor} this
26529              * @param {String} html
26530              */
26531             push: true,
26532              /**
26533              * @event editmodechange
26534              * Fires when the editor switches edit modes
26535              * @param {HtmlEditor} this
26536              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26537              */
26538             editmodechange: true,
26539             /**
26540              * @event editorevent
26541              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26542              * @param {HtmlEditor} this
26543              */
26544             editorevent: true,
26545             /**
26546              * @event firstfocus
26547              * Fires when on first focus - needed by toolbars..
26548              * @param {HtmlEditor} this
26549              */
26550             firstfocus: true,
26551             /**
26552              * @event autosave
26553              * Auto save the htmlEditor value as a file into Events
26554              * @param {HtmlEditor} this
26555              */
26556             autosave: true,
26557             /**
26558              * @event savedpreview
26559              * preview the saved version of htmlEditor
26560              * @param {HtmlEditor} this
26561              */
26562             savedpreview: true,
26563             
26564             /**
26565             * @event stylesheetsclick
26566             * Fires when press the Sytlesheets button
26567             * @param {Roo.HtmlEditorCore} this
26568             */
26569             stylesheetsclick: true
26570         });
26571         this.defaultAutoCreate =  {
26572             tag: "textarea",
26573             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26574             autocomplete: "new-password"
26575         };
26576     },
26577
26578     /**
26579      * Protected method that will not generally be called directly. It
26580      * is called when the editor creates its toolbar. Override this method if you need to
26581      * add custom toolbar buttons.
26582      * @param {HtmlEditor} editor
26583      */
26584     createToolbar : function(editor){
26585         Roo.log("create toolbars");
26586         if (!editor.toolbars || !editor.toolbars.length) {
26587             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26588         }
26589         
26590         for (var i =0 ; i < editor.toolbars.length;i++) {
26591             editor.toolbars[i] = Roo.factory(
26592                     typeof(editor.toolbars[i]) == 'string' ?
26593                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26594                 Roo.form.HtmlEditor);
26595             editor.toolbars[i].init(editor);
26596         }
26597          
26598         
26599     },
26600
26601      
26602     // private
26603     onRender : function(ct, position)
26604     {
26605         var _t = this;
26606         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26607         
26608         this.wrap = this.el.wrap({
26609             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26610         });
26611         
26612         this.editorcore.onRender(ct, position);
26613          
26614         if (this.resizable) {
26615             this.resizeEl = new Roo.Resizable(this.wrap, {
26616                 pinned : true,
26617                 wrap: true,
26618                 dynamic : true,
26619                 minHeight : this.height,
26620                 height: this.height,
26621                 handles : this.resizable,
26622                 width: this.width,
26623                 listeners : {
26624                     resize : function(r, w, h) {
26625                         _t.onResize(w,h); // -something
26626                     }
26627                 }
26628             });
26629             
26630         }
26631         this.createToolbar(this);
26632        
26633         
26634         if(!this.width){
26635             this.setSize(this.wrap.getSize());
26636         }
26637         if (this.resizeEl) {
26638             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26639             // should trigger onReize..
26640         }
26641         
26642         this.keyNav = new Roo.KeyNav(this.el, {
26643             
26644             "tab" : function(e){
26645                 e.preventDefault();
26646                 
26647                 var value = this.getValue();
26648                 
26649                 var start = this.el.dom.selectionStart;
26650                 var end = this.el.dom.selectionEnd;
26651                 
26652                 if(!e.shiftKey){
26653                     
26654                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26655                     this.el.dom.setSelectionRange(end + 1, end + 1);
26656                     return;
26657                 }
26658                 
26659                 var f = value.substring(0, start).split("\t");
26660                 
26661                 if(f.pop().length != 0){
26662                     return;
26663                 }
26664                 
26665                 this.setValue(f.join("\t") + value.substring(end));
26666                 this.el.dom.setSelectionRange(start - 1, start - 1);
26667                 
26668             },
26669             
26670             "home" : function(e){
26671                 e.preventDefault();
26672                 
26673                 var curr = this.el.dom.selectionStart;
26674                 var lines = this.getValue().split("\n");
26675                 
26676                 if(!lines.length){
26677                     return;
26678                 }
26679                 
26680                 if(e.ctrlKey){
26681                     this.el.dom.setSelectionRange(0, 0);
26682                     return;
26683                 }
26684                 
26685                 var pos = 0;
26686                 
26687                 for (var i = 0; i < lines.length;i++) {
26688                     pos += lines[i].length;
26689                     
26690                     if(i != 0){
26691                         pos += 1;
26692                     }
26693                     
26694                     if(pos < curr){
26695                         continue;
26696                     }
26697                     
26698                     pos -= lines[i].length;
26699                     
26700                     break;
26701                 }
26702                 
26703                 if(!e.shiftKey){
26704                     this.el.dom.setSelectionRange(pos, pos);
26705                     return;
26706                 }
26707                 
26708                 this.el.dom.selectionStart = pos;
26709                 this.el.dom.selectionEnd = curr;
26710             },
26711             
26712             "end" : function(e){
26713                 e.preventDefault();
26714                 
26715                 var curr = this.el.dom.selectionStart;
26716                 var lines = this.getValue().split("\n");
26717                 
26718                 if(!lines.length){
26719                     return;
26720                 }
26721                 
26722                 if(e.ctrlKey){
26723                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26724                     return;
26725                 }
26726                 
26727                 var pos = 0;
26728                 
26729                 for (var i = 0; i < lines.length;i++) {
26730                     
26731                     pos += lines[i].length;
26732                     
26733                     if(i != 0){
26734                         pos += 1;
26735                     }
26736                     
26737                     if(pos < curr){
26738                         continue;
26739                     }
26740                     
26741                     break;
26742                 }
26743                 
26744                 if(!e.shiftKey){
26745                     this.el.dom.setSelectionRange(pos, pos);
26746                     return;
26747                 }
26748                 
26749                 this.el.dom.selectionStart = curr;
26750                 this.el.dom.selectionEnd = pos;
26751             },
26752
26753             scope : this,
26754
26755             doRelay : function(foo, bar, hname){
26756                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26757             },
26758
26759             forceKeyDown: true
26760         });
26761         
26762 //        if(this.autosave && this.w){
26763 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26764 //        }
26765     },
26766
26767     // private
26768     onResize : function(w, h)
26769     {
26770         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26771         var ew = false;
26772         var eh = false;
26773         
26774         if(this.el ){
26775             if(typeof w == 'number'){
26776                 var aw = w - this.wrap.getFrameWidth('lr');
26777                 this.el.setWidth(this.adjustWidth('textarea', aw));
26778                 ew = aw;
26779             }
26780             if(typeof h == 'number'){
26781                 var tbh = 0;
26782                 for (var i =0; i < this.toolbars.length;i++) {
26783                     // fixme - ask toolbars for heights?
26784                     tbh += this.toolbars[i].tb.el.getHeight();
26785                     if (this.toolbars[i].footer) {
26786                         tbh += this.toolbars[i].footer.el.getHeight();
26787                     }
26788                 }
26789                 
26790                 
26791                 
26792                 
26793                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26794                 ah -= 5; // knock a few pixes off for look..
26795 //                Roo.log(ah);
26796                 this.el.setHeight(this.adjustWidth('textarea', ah));
26797                 var eh = ah;
26798             }
26799         }
26800         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26801         this.editorcore.onResize(ew,eh);
26802         
26803     },
26804
26805     /**
26806      * Toggles the editor between standard and source edit mode.
26807      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26808      */
26809     toggleSourceEdit : function(sourceEditMode)
26810     {
26811         this.editorcore.toggleSourceEdit(sourceEditMode);
26812         
26813         if(this.editorcore.sourceEditMode){
26814             Roo.log('editor - showing textarea');
26815             
26816 //            Roo.log('in');
26817 //            Roo.log(this.syncValue());
26818             this.editorcore.syncValue();
26819             this.el.removeClass('x-hidden');
26820             this.el.dom.removeAttribute('tabIndex');
26821             this.el.focus();
26822             
26823             for (var i = 0; i < this.toolbars.length; i++) {
26824                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26825                     this.toolbars[i].tb.hide();
26826                     this.toolbars[i].footer.hide();
26827                 }
26828             }
26829             
26830         }else{
26831             Roo.log('editor - hiding textarea');
26832 //            Roo.log('out')
26833 //            Roo.log(this.pushValue()); 
26834             this.editorcore.pushValue();
26835             
26836             this.el.addClass('x-hidden');
26837             this.el.dom.setAttribute('tabIndex', -1);
26838             
26839             for (var i = 0; i < this.toolbars.length; i++) {
26840                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26841                     this.toolbars[i].tb.show();
26842                     this.toolbars[i].footer.show();
26843                 }
26844             }
26845             
26846             //this.deferFocus();
26847         }
26848         
26849         this.setSize(this.wrap.getSize());
26850         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26851         
26852         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26853     },
26854  
26855     // private (for BoxComponent)
26856     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26857
26858     // private (for BoxComponent)
26859     getResizeEl : function(){
26860         return this.wrap;
26861     },
26862
26863     // private (for BoxComponent)
26864     getPositionEl : function(){
26865         return this.wrap;
26866     },
26867
26868     // private
26869     initEvents : function(){
26870         this.originalValue = this.getValue();
26871     },
26872
26873     /**
26874      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26875      * @method
26876      */
26877     markInvalid : Roo.emptyFn,
26878     /**
26879      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26880      * @method
26881      */
26882     clearInvalid : Roo.emptyFn,
26883
26884     setValue : function(v){
26885         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26886         this.editorcore.pushValue();
26887     },
26888
26889      
26890     // private
26891     deferFocus : function(){
26892         this.focus.defer(10, this);
26893     },
26894
26895     // doc'ed in Field
26896     focus : function(){
26897         this.editorcore.focus();
26898         
26899     },
26900       
26901
26902     // private
26903     onDestroy : function(){
26904         
26905         
26906         
26907         if(this.rendered){
26908             
26909             for (var i =0; i < this.toolbars.length;i++) {
26910                 // fixme - ask toolbars for heights?
26911                 this.toolbars[i].onDestroy();
26912             }
26913             
26914             this.wrap.dom.innerHTML = '';
26915             this.wrap.remove();
26916         }
26917     },
26918
26919     // private
26920     onFirstFocus : function(){
26921         //Roo.log("onFirstFocus");
26922         this.editorcore.onFirstFocus();
26923          for (var i =0; i < this.toolbars.length;i++) {
26924             this.toolbars[i].onFirstFocus();
26925         }
26926         
26927     },
26928     
26929     // private
26930     syncValue : function()
26931     {
26932         this.editorcore.syncValue();
26933     },
26934     
26935     pushValue : function()
26936     {
26937         this.editorcore.pushValue();
26938     },
26939     
26940     setStylesheets : function(stylesheets)
26941     {
26942         this.editorcore.setStylesheets(stylesheets);
26943     },
26944     
26945     removeStylesheets : function()
26946     {
26947         this.editorcore.removeStylesheets();
26948     }
26949      
26950     
26951     // hide stuff that is not compatible
26952     /**
26953      * @event blur
26954      * @hide
26955      */
26956     /**
26957      * @event change
26958      * @hide
26959      */
26960     /**
26961      * @event focus
26962      * @hide
26963      */
26964     /**
26965      * @event specialkey
26966      * @hide
26967      */
26968     /**
26969      * @cfg {String} fieldClass @hide
26970      */
26971     /**
26972      * @cfg {String} focusClass @hide
26973      */
26974     /**
26975      * @cfg {String} autoCreate @hide
26976      */
26977     /**
26978      * @cfg {String} inputType @hide
26979      */
26980     /**
26981      * @cfg {String} invalidClass @hide
26982      */
26983     /**
26984      * @cfg {String} invalidText @hide
26985      */
26986     /**
26987      * @cfg {String} msgFx @hide
26988      */
26989     /**
26990      * @cfg {String} validateOnBlur @hide
26991      */
26992 });
26993  
26994     // <script type="text/javascript">
26995 /*
26996  * Based on
26997  * Ext JS Library 1.1.1
26998  * Copyright(c) 2006-2007, Ext JS, LLC.
26999  *  
27000  
27001  */
27002
27003 /**
27004  * @class Roo.form.HtmlEditorToolbar1
27005  * Basic Toolbar
27006  * 
27007  * Usage:
27008  *
27009  new Roo.form.HtmlEditor({
27010     ....
27011     toolbars : [
27012         new Roo.form.HtmlEditorToolbar1({
27013             disable : { fonts: 1 , format: 1, ..., ... , ...],
27014             btns : [ .... ]
27015         })
27016     }
27017      
27018  * 
27019  * @cfg {Object} disable List of elements to disable..
27020  * @cfg {Array} btns List of additional buttons.
27021  * 
27022  * 
27023  * NEEDS Extra CSS? 
27024  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27025  */
27026  
27027 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27028 {
27029     
27030     Roo.apply(this, config);
27031     
27032     // default disabled, based on 'good practice'..
27033     this.disable = this.disable || {};
27034     Roo.applyIf(this.disable, {
27035         fontSize : true,
27036         colors : true,
27037         specialElements : true
27038     });
27039     
27040     
27041     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27042     // dont call parent... till later.
27043 }
27044
27045 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
27046     
27047     tb: false,
27048     
27049     rendered: false,
27050     
27051     editor : false,
27052     editorcore : false,
27053     /**
27054      * @cfg {Object} disable  List of toolbar elements to disable
27055          
27056      */
27057     disable : false,
27058     
27059     
27060      /**
27061      * @cfg {String} createLinkText The default text for the create link prompt
27062      */
27063     createLinkText : 'Please enter the URL for the link:',
27064     /**
27065      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27066      */
27067     defaultLinkValue : 'http:/'+'/',
27068    
27069     
27070       /**
27071      * @cfg {Array} fontFamilies An array of available font families
27072      */
27073     fontFamilies : [
27074         'Arial',
27075         'Courier New',
27076         'Tahoma',
27077         'Times New Roman',
27078         'Verdana'
27079     ],
27080     
27081     specialChars : [
27082            "&#169;",
27083           "&#174;",     
27084           "&#8482;",    
27085           "&#163;" ,    
27086          // "&#8212;",    
27087           "&#8230;",    
27088           "&#247;" ,    
27089         //  "&#225;" ,     ?? a acute?
27090            "&#8364;"    , //Euro
27091        //   "&#8220;"    ,
27092         //  "&#8221;"    ,
27093         //  "&#8226;"    ,
27094           "&#176;"  //   , // degrees
27095
27096          // "&#233;"     , // e ecute
27097          // "&#250;"     , // u ecute?
27098     ],
27099     
27100     specialElements : [
27101         {
27102             text: "Insert Table",
27103             xtype: 'MenuItem',
27104             xns : Roo.Menu,
27105             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27106                 
27107         },
27108         {    
27109             text: "Insert Image",
27110             xtype: 'MenuItem',
27111             xns : Roo.Menu,
27112             ihtml : '<img src="about:blank"/>'
27113             
27114         }
27115         
27116          
27117     ],
27118     
27119     
27120     inputElements : [ 
27121             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27122             "input:submit", "input:button", "select", "textarea", "label" ],
27123     formats : [
27124         ["p"] ,  
27125         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27126         ["pre"],[ "code"], 
27127         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27128         ['div'],['span']
27129     ],
27130     
27131     cleanStyles : [
27132         "font-size"
27133     ],
27134      /**
27135      * @cfg {String} defaultFont default font to use.
27136      */
27137     defaultFont: 'tahoma',
27138    
27139     fontSelect : false,
27140     
27141     
27142     formatCombo : false,
27143     
27144     init : function(editor)
27145     {
27146         this.editor = editor;
27147         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27148         var editorcore = this.editorcore;
27149         
27150         var _t = this;
27151         
27152         var fid = editorcore.frameId;
27153         var etb = this;
27154         function btn(id, toggle, handler){
27155             var xid = fid + '-'+ id ;
27156             return {
27157                 id : xid,
27158                 cmd : id,
27159                 cls : 'x-btn-icon x-edit-'+id,
27160                 enableToggle:toggle !== false,
27161                 scope: _t, // was editor...
27162                 handler:handler||_t.relayBtnCmd,
27163                 clickEvent:'mousedown',
27164                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27165                 tabIndex:-1
27166             };
27167         }
27168         
27169         
27170         
27171         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27172         this.tb = tb;
27173          // stop form submits
27174         tb.el.on('click', function(e){
27175             e.preventDefault(); // what does this do?
27176         });
27177
27178         if(!this.disable.font) { // && !Roo.isSafari){
27179             /* why no safari for fonts 
27180             editor.fontSelect = tb.el.createChild({
27181                 tag:'select',
27182                 tabIndex: -1,
27183                 cls:'x-font-select',
27184                 html: this.createFontOptions()
27185             });
27186             
27187             editor.fontSelect.on('change', function(){
27188                 var font = editor.fontSelect.dom.value;
27189                 editor.relayCmd('fontname', font);
27190                 editor.deferFocus();
27191             }, editor);
27192             
27193             tb.add(
27194                 editor.fontSelect.dom,
27195                 '-'
27196             );
27197             */
27198             
27199         };
27200         if(!this.disable.formats){
27201             this.formatCombo = new Roo.form.ComboBox({
27202                 store: new Roo.data.SimpleStore({
27203                     id : 'tag',
27204                     fields: ['tag'],
27205                     data : this.formats // from states.js
27206                 }),
27207                 blockFocus : true,
27208                 name : '',
27209                 //autoCreate : {tag: "div",  size: "20"},
27210                 displayField:'tag',
27211                 typeAhead: false,
27212                 mode: 'local',
27213                 editable : false,
27214                 triggerAction: 'all',
27215                 emptyText:'Add tag',
27216                 selectOnFocus:true,
27217                 width:135,
27218                 listeners : {
27219                     'select': function(c, r, i) {
27220                         editorcore.insertTag(r.get('tag'));
27221                         editor.focus();
27222                     }
27223                 }
27224
27225             });
27226             tb.addField(this.formatCombo);
27227             
27228         }
27229         
27230         if(!this.disable.format){
27231             tb.add(
27232                 btn('bold'),
27233                 btn('italic'),
27234                 btn('underline'),
27235                 btn('strikethrough')
27236             );
27237         };
27238         if(!this.disable.fontSize){
27239             tb.add(
27240                 '-',
27241                 
27242                 
27243                 btn('increasefontsize', false, editorcore.adjustFont),
27244                 btn('decreasefontsize', false, editorcore.adjustFont)
27245             );
27246         };
27247         
27248         
27249         if(!this.disable.colors){
27250             tb.add(
27251                 '-', {
27252                     id:editorcore.frameId +'-forecolor',
27253                     cls:'x-btn-icon x-edit-forecolor',
27254                     clickEvent:'mousedown',
27255                     tooltip: this.buttonTips['forecolor'] || undefined,
27256                     tabIndex:-1,
27257                     menu : new Roo.menu.ColorMenu({
27258                         allowReselect: true,
27259                         focus: Roo.emptyFn,
27260                         value:'000000',
27261                         plain:true,
27262                         selectHandler: function(cp, color){
27263                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27264                             editor.deferFocus();
27265                         },
27266                         scope: editorcore,
27267                         clickEvent:'mousedown'
27268                     })
27269                 }, {
27270                     id:editorcore.frameId +'backcolor',
27271                     cls:'x-btn-icon x-edit-backcolor',
27272                     clickEvent:'mousedown',
27273                     tooltip: this.buttonTips['backcolor'] || undefined,
27274                     tabIndex:-1,
27275                     menu : new Roo.menu.ColorMenu({
27276                         focus: Roo.emptyFn,
27277                         value:'FFFFFF',
27278                         plain:true,
27279                         allowReselect: true,
27280                         selectHandler: function(cp, color){
27281                             if(Roo.isGecko){
27282                                 editorcore.execCmd('useCSS', false);
27283                                 editorcore.execCmd('hilitecolor', color);
27284                                 editorcore.execCmd('useCSS', true);
27285                                 editor.deferFocus();
27286                             }else{
27287                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27288                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27289                                 editor.deferFocus();
27290                             }
27291                         },
27292                         scope:editorcore,
27293                         clickEvent:'mousedown'
27294                     })
27295                 }
27296             );
27297         };
27298         // now add all the items...
27299         
27300
27301         if(!this.disable.alignments){
27302             tb.add(
27303                 '-',
27304                 btn('justifyleft'),
27305                 btn('justifycenter'),
27306                 btn('justifyright')
27307             );
27308         };
27309
27310         //if(!Roo.isSafari){
27311             if(!this.disable.links){
27312                 tb.add(
27313                     '-',
27314                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27315                 );
27316             };
27317
27318             if(!this.disable.lists){
27319                 tb.add(
27320                     '-',
27321                     btn('insertorderedlist'),
27322                     btn('insertunorderedlist')
27323                 );
27324             }
27325             if(!this.disable.sourceEdit){
27326                 tb.add(
27327                     '-',
27328                     btn('sourceedit', true, function(btn){
27329                         this.toggleSourceEdit(btn.pressed);
27330                     })
27331                 );
27332             }
27333         //}
27334         
27335         var smenu = { };
27336         // special menu.. - needs to be tidied up..
27337         if (!this.disable.special) {
27338             smenu = {
27339                 text: "&#169;",
27340                 cls: 'x-edit-none',
27341                 
27342                 menu : {
27343                     items : []
27344                 }
27345             };
27346             for (var i =0; i < this.specialChars.length; i++) {
27347                 smenu.menu.items.push({
27348                     
27349                     html: this.specialChars[i],
27350                     handler: function(a,b) {
27351                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27352                         //editor.insertAtCursor(a.html);
27353                         
27354                     },
27355                     tabIndex:-1
27356                 });
27357             }
27358             
27359             
27360             tb.add(smenu);
27361             
27362             
27363         }
27364         
27365         var cmenu = { };
27366         if (!this.disable.cleanStyles) {
27367             cmenu = {
27368                 cls: 'x-btn-icon x-btn-clear',
27369                 
27370                 menu : {
27371                     items : []
27372                 }
27373             };
27374             for (var i =0; i < this.cleanStyles.length; i++) {
27375                 cmenu.menu.items.push({
27376                     actiontype : this.cleanStyles[i],
27377                     html: 'Remove ' + this.cleanStyles[i],
27378                     handler: function(a,b) {
27379 //                        Roo.log(a);
27380 //                        Roo.log(b);
27381                         var c = Roo.get(editorcore.doc.body);
27382                         c.select('[style]').each(function(s) {
27383                             s.dom.style.removeProperty(a.actiontype);
27384                         });
27385                         editorcore.syncValue();
27386                     },
27387                     tabIndex:-1
27388                 });
27389             }
27390              cmenu.menu.items.push({
27391                 actiontype : 'tablewidths',
27392                 html: 'Remove Table Widths',
27393                 handler: function(a,b) {
27394                     editorcore.cleanTableWidths();
27395                     editorcore.syncValue();
27396                 },
27397                 tabIndex:-1
27398             });
27399             cmenu.menu.items.push({
27400                 actiontype : 'word',
27401                 html: 'Remove MS Word Formating',
27402                 handler: function(a,b) {
27403                     editorcore.cleanWord();
27404                     editorcore.syncValue();
27405                 },
27406                 tabIndex:-1
27407             });
27408             
27409             cmenu.menu.items.push({
27410                 actiontype : 'all',
27411                 html: 'Remove All Styles',
27412                 handler: function(a,b) {
27413                     
27414                     var c = Roo.get(editorcore.doc.body);
27415                     c.select('[style]').each(function(s) {
27416                         s.dom.removeAttribute('style');
27417                     });
27418                     editorcore.syncValue();
27419                 },
27420                 tabIndex:-1
27421             });
27422             
27423             cmenu.menu.items.push({
27424                 actiontype : 'all',
27425                 html: 'Remove All CSS Classes',
27426                 handler: function(a,b) {
27427                     
27428                     var c = Roo.get(editorcore.doc.body);
27429                     c.select('[class]').each(function(s) {
27430                         s.dom.className = '';
27431                     });
27432                     editorcore.syncValue();
27433                 },
27434                 tabIndex:-1
27435             });
27436             
27437              cmenu.menu.items.push({
27438                 actiontype : 'tidy',
27439                 html: 'Tidy HTML Source',
27440                 handler: function(a,b) {
27441                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27442                     editorcore.syncValue();
27443                 },
27444                 tabIndex:-1
27445             });
27446             
27447             
27448             tb.add(cmenu);
27449         }
27450          
27451         if (!this.disable.specialElements) {
27452             var semenu = {
27453                 text: "Other;",
27454                 cls: 'x-edit-none',
27455                 menu : {
27456                     items : []
27457                 }
27458             };
27459             for (var i =0; i < this.specialElements.length; i++) {
27460                 semenu.menu.items.push(
27461                     Roo.apply({ 
27462                         handler: function(a,b) {
27463                             editor.insertAtCursor(this.ihtml);
27464                         }
27465                     }, this.specialElements[i])
27466                 );
27467                     
27468             }
27469             
27470             tb.add(semenu);
27471             
27472             
27473         }
27474          
27475         
27476         if (this.btns) {
27477             for(var i =0; i< this.btns.length;i++) {
27478                 var b = Roo.factory(this.btns[i],Roo.form);
27479                 b.cls =  'x-edit-none';
27480                 
27481                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27482                     b.cls += ' x-init-enable';
27483                 }
27484                 
27485                 b.scope = editorcore;
27486                 tb.add(b);
27487             }
27488         
27489         }
27490         
27491         
27492         
27493         // disable everything...
27494         
27495         this.tb.items.each(function(item){
27496             
27497            if(
27498                 item.id != editorcore.frameId+ '-sourceedit' && 
27499                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27500             ){
27501                 
27502                 item.disable();
27503             }
27504         });
27505         this.rendered = true;
27506         
27507         // the all the btns;
27508         editor.on('editorevent', this.updateToolbar, this);
27509         // other toolbars need to implement this..
27510         //editor.on('editmodechange', this.updateToolbar, this);
27511     },
27512     
27513     
27514     relayBtnCmd : function(btn) {
27515         this.editorcore.relayCmd(btn.cmd);
27516     },
27517     // private used internally
27518     createLink : function(){
27519         Roo.log("create link?");
27520         var url = prompt(this.createLinkText, this.defaultLinkValue);
27521         if(url && url != 'http:/'+'/'){
27522             this.editorcore.relayCmd('createlink', url);
27523         }
27524     },
27525
27526     
27527     /**
27528      * Protected method that will not generally be called directly. It triggers
27529      * a toolbar update by reading the markup state of the current selection in the editor.
27530      */
27531     updateToolbar: function(){
27532
27533         if(!this.editorcore.activated){
27534             this.editor.onFirstFocus();
27535             return;
27536         }
27537
27538         var btns = this.tb.items.map, 
27539             doc = this.editorcore.doc,
27540             frameId = this.editorcore.frameId;
27541
27542         if(!this.disable.font && !Roo.isSafari){
27543             /*
27544             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27545             if(name != this.fontSelect.dom.value){
27546                 this.fontSelect.dom.value = name;
27547             }
27548             */
27549         }
27550         if(!this.disable.format){
27551             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27552             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27553             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27554             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27555         }
27556         if(!this.disable.alignments){
27557             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27558             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27559             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27560         }
27561         if(!Roo.isSafari && !this.disable.lists){
27562             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27563             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27564         }
27565         
27566         var ans = this.editorcore.getAllAncestors();
27567         if (this.formatCombo) {
27568             
27569             
27570             var store = this.formatCombo.store;
27571             this.formatCombo.setValue("");
27572             for (var i =0; i < ans.length;i++) {
27573                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27574                     // select it..
27575                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27576                     break;
27577                 }
27578             }
27579         }
27580         
27581         
27582         
27583         // hides menus... - so this cant be on a menu...
27584         Roo.menu.MenuMgr.hideAll();
27585
27586         //this.editorsyncValue();
27587     },
27588    
27589     
27590     createFontOptions : function(){
27591         var buf = [], fs = this.fontFamilies, ff, lc;
27592         
27593         
27594         
27595         for(var i = 0, len = fs.length; i< len; i++){
27596             ff = fs[i];
27597             lc = ff.toLowerCase();
27598             buf.push(
27599                 '<option value="',lc,'" style="font-family:',ff,';"',
27600                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27601                     ff,
27602                 '</option>'
27603             );
27604         }
27605         return buf.join('');
27606     },
27607     
27608     toggleSourceEdit : function(sourceEditMode){
27609         
27610         Roo.log("toolbar toogle");
27611         if(sourceEditMode === undefined){
27612             sourceEditMode = !this.sourceEditMode;
27613         }
27614         this.sourceEditMode = sourceEditMode === true;
27615         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27616         // just toggle the button?
27617         if(btn.pressed !== this.sourceEditMode){
27618             btn.toggle(this.sourceEditMode);
27619             return;
27620         }
27621         
27622         if(sourceEditMode){
27623             Roo.log("disabling buttons");
27624             this.tb.items.each(function(item){
27625                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27626                     item.disable();
27627                 }
27628             });
27629           
27630         }else{
27631             Roo.log("enabling buttons");
27632             if(this.editorcore.initialized){
27633                 this.tb.items.each(function(item){
27634                     item.enable();
27635                 });
27636             }
27637             
27638         }
27639         Roo.log("calling toggole on editor");
27640         // tell the editor that it's been pressed..
27641         this.editor.toggleSourceEdit(sourceEditMode);
27642        
27643     },
27644      /**
27645      * Object collection of toolbar tooltips for the buttons in the editor. The key
27646      * is the command id associated with that button and the value is a valid QuickTips object.
27647      * For example:
27648 <pre><code>
27649 {
27650     bold : {
27651         title: 'Bold (Ctrl+B)',
27652         text: 'Make the selected text bold.',
27653         cls: 'x-html-editor-tip'
27654     },
27655     italic : {
27656         title: 'Italic (Ctrl+I)',
27657         text: 'Make the selected text italic.',
27658         cls: 'x-html-editor-tip'
27659     },
27660     ...
27661 </code></pre>
27662     * @type Object
27663      */
27664     buttonTips : {
27665         bold : {
27666             title: 'Bold (Ctrl+B)',
27667             text: 'Make the selected text bold.',
27668             cls: 'x-html-editor-tip'
27669         },
27670         italic : {
27671             title: 'Italic (Ctrl+I)',
27672             text: 'Make the selected text italic.',
27673             cls: 'x-html-editor-tip'
27674         },
27675         underline : {
27676             title: 'Underline (Ctrl+U)',
27677             text: 'Underline the selected text.',
27678             cls: 'x-html-editor-tip'
27679         },
27680         strikethrough : {
27681             title: 'Strikethrough',
27682             text: 'Strikethrough the selected text.',
27683             cls: 'x-html-editor-tip'
27684         },
27685         increasefontsize : {
27686             title: 'Grow Text',
27687             text: 'Increase the font size.',
27688             cls: 'x-html-editor-tip'
27689         },
27690         decreasefontsize : {
27691             title: 'Shrink Text',
27692             text: 'Decrease the font size.',
27693             cls: 'x-html-editor-tip'
27694         },
27695         backcolor : {
27696             title: 'Text Highlight Color',
27697             text: 'Change the background color of the selected text.',
27698             cls: 'x-html-editor-tip'
27699         },
27700         forecolor : {
27701             title: 'Font Color',
27702             text: 'Change the color of the selected text.',
27703             cls: 'x-html-editor-tip'
27704         },
27705         justifyleft : {
27706             title: 'Align Text Left',
27707             text: 'Align text to the left.',
27708             cls: 'x-html-editor-tip'
27709         },
27710         justifycenter : {
27711             title: 'Center Text',
27712             text: 'Center text in the editor.',
27713             cls: 'x-html-editor-tip'
27714         },
27715         justifyright : {
27716             title: 'Align Text Right',
27717             text: 'Align text to the right.',
27718             cls: 'x-html-editor-tip'
27719         },
27720         insertunorderedlist : {
27721             title: 'Bullet List',
27722             text: 'Start a bulleted list.',
27723             cls: 'x-html-editor-tip'
27724         },
27725         insertorderedlist : {
27726             title: 'Numbered List',
27727             text: 'Start a numbered list.',
27728             cls: 'x-html-editor-tip'
27729         },
27730         createlink : {
27731             title: 'Hyperlink',
27732             text: 'Make the selected text a hyperlink.',
27733             cls: 'x-html-editor-tip'
27734         },
27735         sourceedit : {
27736             title: 'Source Edit',
27737             text: 'Switch to source editing mode.',
27738             cls: 'x-html-editor-tip'
27739         }
27740     },
27741     // private
27742     onDestroy : function(){
27743         if(this.rendered){
27744             
27745             this.tb.items.each(function(item){
27746                 if(item.menu){
27747                     item.menu.removeAll();
27748                     if(item.menu.el){
27749                         item.menu.el.destroy();
27750                     }
27751                 }
27752                 item.destroy();
27753             });
27754              
27755         }
27756     },
27757     onFirstFocus: function() {
27758         this.tb.items.each(function(item){
27759            item.enable();
27760         });
27761     }
27762 });
27763
27764
27765
27766
27767 // <script type="text/javascript">
27768 /*
27769  * Based on
27770  * Ext JS Library 1.1.1
27771  * Copyright(c) 2006-2007, Ext JS, LLC.
27772  *  
27773  
27774  */
27775
27776  
27777 /**
27778  * @class Roo.form.HtmlEditor.ToolbarContext
27779  * Context Toolbar
27780  * 
27781  * Usage:
27782  *
27783  new Roo.form.HtmlEditor({
27784     ....
27785     toolbars : [
27786         { xtype: 'ToolbarStandard', styles : {} }
27787         { xtype: 'ToolbarContext', disable : {} }
27788     ]
27789 })
27790
27791      
27792  * 
27793  * @config : {Object} disable List of elements to disable.. (not done yet.)
27794  * @config : {Object} styles  Map of styles available.
27795  * 
27796  */
27797
27798 Roo.form.HtmlEditor.ToolbarContext = function(config)
27799 {
27800     
27801     Roo.apply(this, config);
27802     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27803     // dont call parent... till later.
27804     this.styles = this.styles || {};
27805 }
27806
27807  
27808
27809 Roo.form.HtmlEditor.ToolbarContext.types = {
27810     'IMG' : {
27811         width : {
27812             title: "Width",
27813             width: 40
27814         },
27815         height:  {
27816             title: "Height",
27817             width: 40
27818         },
27819         align: {
27820             title: "Align",
27821             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27822             width : 80
27823             
27824         },
27825         border: {
27826             title: "Border",
27827             width: 40
27828         },
27829         alt: {
27830             title: "Alt",
27831             width: 120
27832         },
27833         src : {
27834             title: "Src",
27835             width: 220
27836         }
27837         
27838     },
27839     'A' : {
27840         name : {
27841             title: "Name",
27842             width: 50
27843         },
27844         target:  {
27845             title: "Target",
27846             width: 120
27847         },
27848         href:  {
27849             title: "Href",
27850             width: 220
27851         } // border?
27852         
27853     },
27854     'TABLE' : {
27855         rows : {
27856             title: "Rows",
27857             width: 20
27858         },
27859         cols : {
27860             title: "Cols",
27861             width: 20
27862         },
27863         width : {
27864             title: "Width",
27865             width: 40
27866         },
27867         height : {
27868             title: "Height",
27869             width: 40
27870         },
27871         border : {
27872             title: "Border",
27873             width: 20
27874         }
27875     },
27876     'TD' : {
27877         width : {
27878             title: "Width",
27879             width: 40
27880         },
27881         height : {
27882             title: "Height",
27883             width: 40
27884         },   
27885         align: {
27886             title: "Align",
27887             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27888             width: 80
27889         },
27890         valign: {
27891             title: "Valign",
27892             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27893             width: 80
27894         },
27895         colspan: {
27896             title: "Colspan",
27897             width: 20
27898             
27899         },
27900          'font-family'  : {
27901             title : "Font",
27902             style : 'fontFamily',
27903             displayField: 'display',
27904             optname : 'font-family',
27905             width: 140
27906         }
27907     },
27908     'INPUT' : {
27909         name : {
27910             title: "name",
27911             width: 120
27912         },
27913         value : {
27914             title: "Value",
27915             width: 120
27916         },
27917         width : {
27918             title: "Width",
27919             width: 40
27920         }
27921     },
27922     'LABEL' : {
27923         'for' : {
27924             title: "For",
27925             width: 120
27926         }
27927     },
27928     'TEXTAREA' : {
27929           name : {
27930             title: "name",
27931             width: 120
27932         },
27933         rows : {
27934             title: "Rows",
27935             width: 20
27936         },
27937         cols : {
27938             title: "Cols",
27939             width: 20
27940         }
27941     },
27942     'SELECT' : {
27943         name : {
27944             title: "name",
27945             width: 120
27946         },
27947         selectoptions : {
27948             title: "Options",
27949             width: 200
27950         }
27951     },
27952     
27953     // should we really allow this??
27954     // should this just be 
27955     'BODY' : {
27956         title : {
27957             title: "Title",
27958             width: 200,
27959             disabled : true
27960         }
27961     },
27962     'SPAN' : {
27963         'font-family'  : {
27964             title : "Font",
27965             style : 'fontFamily',
27966             displayField: 'display',
27967             optname : 'font-family',
27968             width: 140
27969         }
27970     },
27971     'DIV' : {
27972         'font-family'  : {
27973             title : "Font",
27974             style : 'fontFamily',
27975             displayField: 'display',
27976             optname : 'font-family',
27977             width: 140
27978         }
27979     },
27980      'P' : {
27981         'font-family'  : {
27982             title : "Font",
27983             style : 'fontFamily',
27984             displayField: 'display',
27985             optname : 'font-family',
27986             width: 140
27987         }
27988     },
27989     
27990     '*' : {
27991         // empty..
27992     }
27993
27994 };
27995
27996 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27997 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27998
27999 Roo.form.HtmlEditor.ToolbarContext.options = {
28000         'font-family'  : [ 
28001                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28002                 [ 'Courier New', 'Courier New'],
28003                 [ 'Tahoma', 'Tahoma'],
28004                 [ 'Times New Roman,serif', 'Times'],
28005                 [ 'Verdana','Verdana' ]
28006         ]
28007 };
28008
28009 // fixme - these need to be configurable..
28010  
28011
28012 //Roo.form.HtmlEditor.ToolbarContext.types
28013
28014
28015 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28016     
28017     tb: false,
28018     
28019     rendered: false,
28020     
28021     editor : false,
28022     editorcore : false,
28023     /**
28024      * @cfg {Object} disable  List of toolbar elements to disable
28025          
28026      */
28027     disable : false,
28028     /**
28029      * @cfg {Object} styles List of styles 
28030      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28031      *
28032      * These must be defined in the page, so they get rendered correctly..
28033      * .headline { }
28034      * TD.underline { }
28035      * 
28036      */
28037     styles : false,
28038     
28039     options: false,
28040     
28041     toolbars : false,
28042     
28043     init : function(editor)
28044     {
28045         this.editor = editor;
28046         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28047         var editorcore = this.editorcore;
28048         
28049         var fid = editorcore.frameId;
28050         var etb = this;
28051         function btn(id, toggle, handler){
28052             var xid = fid + '-'+ id ;
28053             return {
28054                 id : xid,
28055                 cmd : id,
28056                 cls : 'x-btn-icon x-edit-'+id,
28057                 enableToggle:toggle !== false,
28058                 scope: editorcore, // was editor...
28059                 handler:handler||editorcore.relayBtnCmd,
28060                 clickEvent:'mousedown',
28061                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28062                 tabIndex:-1
28063             };
28064         }
28065         // create a new element.
28066         var wdiv = editor.wrap.createChild({
28067                 tag: 'div'
28068             }, editor.wrap.dom.firstChild.nextSibling, true);
28069         
28070         // can we do this more than once??
28071         
28072          // stop form submits
28073       
28074  
28075         // disable everything...
28076         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28077         this.toolbars = {};
28078            
28079         for (var i in  ty) {
28080           
28081             this.toolbars[i] = this.buildToolbar(ty[i],i);
28082         }
28083         this.tb = this.toolbars.BODY;
28084         this.tb.el.show();
28085         this.buildFooter();
28086         this.footer.show();
28087         editor.on('hide', function( ) { this.footer.hide() }, this);
28088         editor.on('show', function( ) { this.footer.show() }, this);
28089         
28090          
28091         this.rendered = true;
28092         
28093         // the all the btns;
28094         editor.on('editorevent', this.updateToolbar, this);
28095         // other toolbars need to implement this..
28096         //editor.on('editmodechange', this.updateToolbar, this);
28097     },
28098     
28099     
28100     
28101     /**
28102      * Protected method that will not generally be called directly. It triggers
28103      * a toolbar update by reading the markup state of the current selection in the editor.
28104      *
28105      * Note you can force an update by calling on('editorevent', scope, false)
28106      */
28107     updateToolbar: function(editor,ev,sel){
28108
28109         //Roo.log(ev);
28110         // capture mouse up - this is handy for selecting images..
28111         // perhaps should go somewhere else...
28112         if(!this.editorcore.activated){
28113              this.editor.onFirstFocus();
28114             return;
28115         }
28116         
28117         
28118         
28119         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28120         // selectNode - might want to handle IE?
28121         if (ev &&
28122             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28123             ev.target && ev.target.tagName == 'IMG') {
28124             // they have click on an image...
28125             // let's see if we can change the selection...
28126             sel = ev.target;
28127          
28128               var nodeRange = sel.ownerDocument.createRange();
28129             try {
28130                 nodeRange.selectNode(sel);
28131             } catch (e) {
28132                 nodeRange.selectNodeContents(sel);
28133             }
28134             //nodeRange.collapse(true);
28135             var s = this.editorcore.win.getSelection();
28136             s.removeAllRanges();
28137             s.addRange(nodeRange);
28138         }  
28139         
28140       
28141         var updateFooter = sel ? false : true;
28142         
28143         
28144         var ans = this.editorcore.getAllAncestors();
28145         
28146         // pick
28147         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28148         
28149         if (!sel) { 
28150             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28151             sel = sel ? sel : this.editorcore.doc.body;
28152             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28153             
28154         }
28155         // pick a menu that exists..
28156         var tn = sel.tagName.toUpperCase();
28157         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28158         
28159         tn = sel.tagName.toUpperCase();
28160         
28161         var lastSel = this.tb.selectedNode;
28162         
28163         this.tb.selectedNode = sel;
28164         
28165         // if current menu does not match..
28166         
28167         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28168                 
28169             this.tb.el.hide();
28170             ///console.log("show: " + tn);
28171             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28172             this.tb.el.show();
28173             // update name
28174             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28175             
28176             
28177             // update attributes
28178             if (this.tb.fields) {
28179                 this.tb.fields.each(function(e) {
28180                     if (e.stylename) {
28181                         e.setValue(sel.style[e.stylename]);
28182                         return;
28183                     } 
28184                    e.setValue(sel.getAttribute(e.attrname));
28185                 });
28186             }
28187             
28188             var hasStyles = false;
28189             for(var i in this.styles) {
28190                 hasStyles = true;
28191                 break;
28192             }
28193             
28194             // update styles
28195             if (hasStyles) { 
28196                 var st = this.tb.fields.item(0);
28197                 
28198                 st.store.removeAll();
28199                
28200                 
28201                 var cn = sel.className.split(/\s+/);
28202                 
28203                 var avs = [];
28204                 if (this.styles['*']) {
28205                     
28206                     Roo.each(this.styles['*'], function(v) {
28207                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28208                     });
28209                 }
28210                 if (this.styles[tn]) { 
28211                     Roo.each(this.styles[tn], function(v) {
28212                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28213                     });
28214                 }
28215                 
28216                 st.store.loadData(avs);
28217                 st.collapse();
28218                 st.setValue(cn);
28219             }
28220             // flag our selected Node.
28221             this.tb.selectedNode = sel;
28222            
28223            
28224             Roo.menu.MenuMgr.hideAll();
28225
28226         }
28227         
28228         if (!updateFooter) {
28229             //this.footDisp.dom.innerHTML = ''; 
28230             return;
28231         }
28232         // update the footer
28233         //
28234         var html = '';
28235         
28236         this.footerEls = ans.reverse();
28237         Roo.each(this.footerEls, function(a,i) {
28238             if (!a) { return; }
28239             html += html.length ? ' &gt; '  :  '';
28240             
28241             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28242             
28243         });
28244        
28245         // 
28246         var sz = this.footDisp.up('td').getSize();
28247         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28248         this.footDisp.dom.style.marginLeft = '5px';
28249         
28250         this.footDisp.dom.style.overflow = 'hidden';
28251         
28252         this.footDisp.dom.innerHTML = html;
28253             
28254         //this.editorsyncValue();
28255     },
28256      
28257     
28258    
28259        
28260     // private
28261     onDestroy : function(){
28262         if(this.rendered){
28263             
28264             this.tb.items.each(function(item){
28265                 if(item.menu){
28266                     item.menu.removeAll();
28267                     if(item.menu.el){
28268                         item.menu.el.destroy();
28269                     }
28270                 }
28271                 item.destroy();
28272             });
28273              
28274         }
28275     },
28276     onFirstFocus: function() {
28277         // need to do this for all the toolbars..
28278         this.tb.items.each(function(item){
28279            item.enable();
28280         });
28281     },
28282     buildToolbar: function(tlist, nm)
28283     {
28284         var editor = this.editor;
28285         var editorcore = this.editorcore;
28286          // create a new element.
28287         var wdiv = editor.wrap.createChild({
28288                 tag: 'div'
28289             }, editor.wrap.dom.firstChild.nextSibling, true);
28290         
28291        
28292         var tb = new Roo.Toolbar(wdiv);
28293         // add the name..
28294         
28295         tb.add(nm+ ":&nbsp;");
28296         
28297         var styles = [];
28298         for(var i in this.styles) {
28299             styles.push(i);
28300         }
28301         
28302         // styles...
28303         if (styles && styles.length) {
28304             
28305             // this needs a multi-select checkbox...
28306             tb.addField( new Roo.form.ComboBox({
28307                 store: new Roo.data.SimpleStore({
28308                     id : 'val',
28309                     fields: ['val', 'selected'],
28310                     data : [] 
28311                 }),
28312                 name : '-roo-edit-className',
28313                 attrname : 'className',
28314                 displayField: 'val',
28315                 typeAhead: false,
28316                 mode: 'local',
28317                 editable : false,
28318                 triggerAction: 'all',
28319                 emptyText:'Select Style',
28320                 selectOnFocus:true,
28321                 width: 130,
28322                 listeners : {
28323                     'select': function(c, r, i) {
28324                         // initial support only for on class per el..
28325                         tb.selectedNode.className =  r ? r.get('val') : '';
28326                         editorcore.syncValue();
28327                     }
28328                 }
28329     
28330             }));
28331         }
28332         
28333         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28334         var tbops = tbc.options;
28335         
28336         for (var i in tlist) {
28337             
28338             var item = tlist[i];
28339             tb.add(item.title + ":&nbsp;");
28340             
28341             
28342             //optname == used so you can configure the options available..
28343             var opts = item.opts ? item.opts : false;
28344             if (item.optname) {
28345                 opts = tbops[item.optname];
28346            
28347             }
28348             
28349             if (opts) {
28350                 // opts == pulldown..
28351                 tb.addField( new Roo.form.ComboBox({
28352                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28353                         id : 'val',
28354                         fields: ['val', 'display'],
28355                         data : opts  
28356                     }),
28357                     name : '-roo-edit-' + i,
28358                     attrname : i,
28359                     stylename : item.style ? item.style : false,
28360                     displayField: item.displayField ? item.displayField : 'val',
28361                     valueField :  'val',
28362                     typeAhead: false,
28363                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28364                     editable : false,
28365                     triggerAction: 'all',
28366                     emptyText:'Select',
28367                     selectOnFocus:true,
28368                     width: item.width ? item.width  : 130,
28369                     listeners : {
28370                         'select': function(c, r, i) {
28371                             if (c.stylename) {
28372                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28373                                 return;
28374                             }
28375                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28376                         }
28377                     }
28378
28379                 }));
28380                 continue;
28381                     
28382                  
28383                 
28384                 tb.addField( new Roo.form.TextField({
28385                     name: i,
28386                     width: 100,
28387                     //allowBlank:false,
28388                     value: ''
28389                 }));
28390                 continue;
28391             }
28392             tb.addField( new Roo.form.TextField({
28393                 name: '-roo-edit-' + i,
28394                 attrname : i,
28395                 
28396                 width: item.width,
28397                 //allowBlank:true,
28398                 value: '',
28399                 listeners: {
28400                     'change' : function(f, nv, ov) {
28401                         tb.selectedNode.setAttribute(f.attrname, nv);
28402                     }
28403                 }
28404             }));
28405              
28406         }
28407         
28408         var _this = this;
28409         
28410         if(nm == 'BODY'){
28411             tb.addSeparator();
28412         
28413             tb.addButton( {
28414                 text: 'Stylesheets',
28415
28416                 listeners : {
28417                     click : function ()
28418                     {
28419                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28420                     }
28421                 }
28422             });
28423         }
28424         
28425         tb.addFill();
28426         tb.addButton( {
28427             text: 'Remove Tag',
28428     
28429             listeners : {
28430                 click : function ()
28431                 {
28432                     // remove
28433                     // undo does not work.
28434                      
28435                     var sn = tb.selectedNode;
28436                     
28437                     var pn = sn.parentNode;
28438                     
28439                     var stn =  sn.childNodes[0];
28440                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28441                     while (sn.childNodes.length) {
28442                         var node = sn.childNodes[0];
28443                         sn.removeChild(node);
28444                         //Roo.log(node);
28445                         pn.insertBefore(node, sn);
28446                         
28447                     }
28448                     pn.removeChild(sn);
28449                     var range = editorcore.createRange();
28450         
28451                     range.setStart(stn,0);
28452                     range.setEnd(en,0); //????
28453                     //range.selectNode(sel);
28454                     
28455                     
28456                     var selection = editorcore.getSelection();
28457                     selection.removeAllRanges();
28458                     selection.addRange(range);
28459                     
28460                     
28461                     
28462                     //_this.updateToolbar(null, null, pn);
28463                     _this.updateToolbar(null, null, null);
28464                     _this.footDisp.dom.innerHTML = ''; 
28465                 }
28466             }
28467             
28468                     
28469                 
28470             
28471         });
28472         
28473         
28474         tb.el.on('click', function(e){
28475             e.preventDefault(); // what does this do?
28476         });
28477         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28478         tb.el.hide();
28479         tb.name = nm;
28480         // dont need to disable them... as they will get hidden
28481         return tb;
28482          
28483         
28484     },
28485     buildFooter : function()
28486     {
28487         
28488         var fel = this.editor.wrap.createChild();
28489         this.footer = new Roo.Toolbar(fel);
28490         // toolbar has scrolly on left / right?
28491         var footDisp= new Roo.Toolbar.Fill();
28492         var _t = this;
28493         this.footer.add(
28494             {
28495                 text : '&lt;',
28496                 xtype: 'Button',
28497                 handler : function() {
28498                     _t.footDisp.scrollTo('left',0,true)
28499                 }
28500             }
28501         );
28502         this.footer.add( footDisp );
28503         this.footer.add( 
28504             {
28505                 text : '&gt;',
28506                 xtype: 'Button',
28507                 handler : function() {
28508                     // no animation..
28509                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28510                 }
28511             }
28512         );
28513         var fel = Roo.get(footDisp.el);
28514         fel.addClass('x-editor-context');
28515         this.footDispWrap = fel; 
28516         this.footDispWrap.overflow  = 'hidden';
28517         
28518         this.footDisp = fel.createChild();
28519         this.footDispWrap.on('click', this.onContextClick, this)
28520         
28521         
28522     },
28523     onContextClick : function (ev,dom)
28524     {
28525         ev.preventDefault();
28526         var  cn = dom.className;
28527         //Roo.log(cn);
28528         if (!cn.match(/x-ed-loc-/)) {
28529             return;
28530         }
28531         var n = cn.split('-').pop();
28532         var ans = this.footerEls;
28533         var sel = ans[n];
28534         
28535          // pick
28536         var range = this.editorcore.createRange();
28537         
28538         range.selectNodeContents(sel);
28539         //range.selectNode(sel);
28540         
28541         
28542         var selection = this.editorcore.getSelection();
28543         selection.removeAllRanges();
28544         selection.addRange(range);
28545         
28546         
28547         
28548         this.updateToolbar(null, null, sel);
28549         
28550         
28551     }
28552     
28553     
28554     
28555     
28556     
28557 });
28558
28559
28560
28561
28562
28563 /*
28564  * Based on:
28565  * Ext JS Library 1.1.1
28566  * Copyright(c) 2006-2007, Ext JS, LLC.
28567  *
28568  * Originally Released Under LGPL - original licence link has changed is not relivant.
28569  *
28570  * Fork - LGPL
28571  * <script type="text/javascript">
28572  */
28573  
28574 /**
28575  * @class Roo.form.BasicForm
28576  * @extends Roo.util.Observable
28577  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28578  * @constructor
28579  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28580  * @param {Object} config Configuration options
28581  */
28582 Roo.form.BasicForm = function(el, config){
28583     this.allItems = [];
28584     this.childForms = [];
28585     Roo.apply(this, config);
28586     /*
28587      * The Roo.form.Field items in this form.
28588      * @type MixedCollection
28589      */
28590      
28591      
28592     this.items = new Roo.util.MixedCollection(false, function(o){
28593         return o.id || (o.id = Roo.id());
28594     });
28595     this.addEvents({
28596         /**
28597          * @event beforeaction
28598          * Fires before any action is performed. Return false to cancel the action.
28599          * @param {Form} this
28600          * @param {Action} action The action to be performed
28601          */
28602         beforeaction: true,
28603         /**
28604          * @event actionfailed
28605          * Fires when an action fails.
28606          * @param {Form} this
28607          * @param {Action} action The action that failed
28608          */
28609         actionfailed : true,
28610         /**
28611          * @event actioncomplete
28612          * Fires when an action is completed.
28613          * @param {Form} this
28614          * @param {Action} action The action that completed
28615          */
28616         actioncomplete : true
28617     });
28618     if(el){
28619         this.initEl(el);
28620     }
28621     Roo.form.BasicForm.superclass.constructor.call(this);
28622 };
28623
28624 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28625     /**
28626      * @cfg {String} method
28627      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28628      */
28629     /**
28630      * @cfg {DataReader} reader
28631      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28632      * This is optional as there is built-in support for processing JSON.
28633      */
28634     /**
28635      * @cfg {DataReader} errorReader
28636      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28637      * This is completely optional as there is built-in support for processing JSON.
28638      */
28639     /**
28640      * @cfg {String} url
28641      * The URL to use for form actions if one isn't supplied in the action options.
28642      */
28643     /**
28644      * @cfg {Boolean} fileUpload
28645      * Set to true if this form is a file upload.
28646      */
28647      
28648     /**
28649      * @cfg {Object} baseParams
28650      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28651      */
28652      /**
28653      
28654     /**
28655      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28656      */
28657     timeout: 30,
28658
28659     // private
28660     activeAction : null,
28661
28662     /**
28663      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28664      * or setValues() data instead of when the form was first created.
28665      */
28666     trackResetOnLoad : false,
28667     
28668     
28669     /**
28670      * childForms - used for multi-tab forms
28671      * @type {Array}
28672      */
28673     childForms : false,
28674     
28675     /**
28676      * allItems - full list of fields.
28677      * @type {Array}
28678      */
28679     allItems : false,
28680     
28681     /**
28682      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28683      * element by passing it or its id or mask the form itself by passing in true.
28684      * @type Mixed
28685      */
28686     waitMsgTarget : false,
28687
28688     // private
28689     initEl : function(el){
28690         this.el = Roo.get(el);
28691         this.id = this.el.id || Roo.id();
28692         this.el.on('submit', this.onSubmit, this);
28693         this.el.addClass('x-form');
28694     },
28695
28696     // private
28697     onSubmit : function(e){
28698         e.stopEvent();
28699     },
28700
28701     /**
28702      * Returns true if client-side validation on the form is successful.
28703      * @return Boolean
28704      */
28705     isValid : function(){
28706         var valid = true;
28707         this.items.each(function(f){
28708            if(!f.validate()){
28709                valid = false;
28710            }
28711         });
28712         return valid;
28713     },
28714
28715     /**
28716      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
28717      * @return Boolean
28718      */
28719     isDirty : function(){
28720         var dirty = false;
28721         this.items.each(function(f){
28722            if(f.isDirty()){
28723                dirty = true;
28724                return false;
28725            }
28726         });
28727         return dirty;
28728     },
28729     
28730     /**
28731      * Returns true if any fields in this form have changed since their original load. (New version)
28732      * @return Boolean
28733      */
28734     
28735     hasChanged : function()
28736     {
28737         var dirty = false;
28738         this.items.each(function(f){
28739            if(f.hasChanged()){
28740                dirty = true;
28741                return false;
28742            }
28743         });
28744         return dirty;
28745         
28746     },
28747     /**
28748      * Resets all hasChanged to 'false' -
28749      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
28750      * So hasChanged storage is only to be used for this purpose
28751      * @return Boolean
28752      */
28753     resetHasChanged : function()
28754     {
28755         this.items.each(function(f){
28756            f.resetHasChanged();
28757         });
28758         
28759     },
28760     
28761     
28762     /**
28763      * Performs a predefined action (submit or load) or custom actions you define on this form.
28764      * @param {String} actionName The name of the action type
28765      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28766      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28767      * accept other config options):
28768      * <pre>
28769 Property          Type             Description
28770 ----------------  ---------------  ----------------------------------------------------------------------------------
28771 url               String           The url for the action (defaults to the form's url)
28772 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28773 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28774 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28775                                    validate the form on the client (defaults to false)
28776      * </pre>
28777      * @return {BasicForm} this
28778      */
28779     doAction : function(action, options){
28780         if(typeof action == 'string'){
28781             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28782         }
28783         if(this.fireEvent('beforeaction', this, action) !== false){
28784             this.beforeAction(action);
28785             action.run.defer(100, action);
28786         }
28787         return this;
28788     },
28789
28790     /**
28791      * Shortcut to do a submit action.
28792      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28793      * @return {BasicForm} this
28794      */
28795     submit : function(options){
28796         this.doAction('submit', options);
28797         return this;
28798     },
28799
28800     /**
28801      * Shortcut to do a load action.
28802      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28803      * @return {BasicForm} this
28804      */
28805     load : function(options){
28806         this.doAction('load', options);
28807         return this;
28808     },
28809
28810     /**
28811      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28812      * @param {Record} record The record to edit
28813      * @return {BasicForm} this
28814      */
28815     updateRecord : function(record){
28816         record.beginEdit();
28817         var fs = record.fields;
28818         fs.each(function(f){
28819             var field = this.findField(f.name);
28820             if(field){
28821                 record.set(f.name, field.getValue());
28822             }
28823         }, this);
28824         record.endEdit();
28825         return this;
28826     },
28827
28828     /**
28829      * Loads an Roo.data.Record into this form.
28830      * @param {Record} record The record to load
28831      * @return {BasicForm} this
28832      */
28833     loadRecord : function(record){
28834         this.setValues(record.data);
28835         return this;
28836     },
28837
28838     // private
28839     beforeAction : function(action){
28840         var o = action.options;
28841         
28842        
28843         if(this.waitMsgTarget === true){
28844             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28845         }else if(this.waitMsgTarget){
28846             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28847             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28848         }else {
28849             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28850         }
28851          
28852     },
28853
28854     // private
28855     afterAction : function(action, success){
28856         this.activeAction = null;
28857         var o = action.options;
28858         
28859         if(this.waitMsgTarget === true){
28860             this.el.unmask();
28861         }else if(this.waitMsgTarget){
28862             this.waitMsgTarget.unmask();
28863         }else{
28864             Roo.MessageBox.updateProgress(1);
28865             Roo.MessageBox.hide();
28866         }
28867          
28868         if(success){
28869             if(o.reset){
28870                 this.reset();
28871             }
28872             Roo.callback(o.success, o.scope, [this, action]);
28873             this.fireEvent('actioncomplete', this, action);
28874             
28875         }else{
28876             
28877             // failure condition..
28878             // we have a scenario where updates need confirming.
28879             // eg. if a locking scenario exists..
28880             // we look for { errors : { needs_confirm : true }} in the response.
28881             if (
28882                 (typeof(action.result) != 'undefined')  &&
28883                 (typeof(action.result.errors) != 'undefined')  &&
28884                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28885            ){
28886                 var _t = this;
28887                 Roo.MessageBox.confirm(
28888                     "Change requires confirmation",
28889                     action.result.errorMsg,
28890                     function(r) {
28891                         if (r != 'yes') {
28892                             return;
28893                         }
28894                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28895                     }
28896                     
28897                 );
28898                 
28899                 
28900                 
28901                 return;
28902             }
28903             
28904             Roo.callback(o.failure, o.scope, [this, action]);
28905             // show an error message if no failed handler is set..
28906             if (!this.hasListener('actionfailed')) {
28907                 Roo.MessageBox.alert("Error",
28908                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28909                         action.result.errorMsg :
28910                         "Saving Failed, please check your entries or try again"
28911                 );
28912             }
28913             
28914             this.fireEvent('actionfailed', this, action);
28915         }
28916         
28917     },
28918
28919     /**
28920      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28921      * @param {String} id The value to search for
28922      * @return Field
28923      */
28924     findField : function(id){
28925         var field = this.items.get(id);
28926         if(!field){
28927             this.items.each(function(f){
28928                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28929                     field = f;
28930                     return false;
28931                 }
28932             });
28933         }
28934         return field || null;
28935     },
28936
28937     /**
28938      * Add a secondary form to this one, 
28939      * Used to provide tabbed forms. One form is primary, with hidden values 
28940      * which mirror the elements from the other forms.
28941      * 
28942      * @param {Roo.form.Form} form to add.
28943      * 
28944      */
28945     addForm : function(form)
28946     {
28947        
28948         if (this.childForms.indexOf(form) > -1) {
28949             // already added..
28950             return;
28951         }
28952         this.childForms.push(form);
28953         var n = '';
28954         Roo.each(form.allItems, function (fe) {
28955             
28956             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28957             if (this.findField(n)) { // already added..
28958                 return;
28959             }
28960             var add = new Roo.form.Hidden({
28961                 name : n
28962             });
28963             add.render(this.el);
28964             
28965             this.add( add );
28966         }, this);
28967         
28968     },
28969     /**
28970      * Mark fields in this form invalid in bulk.
28971      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28972      * @return {BasicForm} this
28973      */
28974     markInvalid : function(errors){
28975         if(errors instanceof Array){
28976             for(var i = 0, len = errors.length; i < len; i++){
28977                 var fieldError = errors[i];
28978                 var f = this.findField(fieldError.id);
28979                 if(f){
28980                     f.markInvalid(fieldError.msg);
28981                 }
28982             }
28983         }else{
28984             var field, id;
28985             for(id in errors){
28986                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28987                     field.markInvalid(errors[id]);
28988                 }
28989             }
28990         }
28991         Roo.each(this.childForms || [], function (f) {
28992             f.markInvalid(errors);
28993         });
28994         
28995         return this;
28996     },
28997
28998     /**
28999      * Set values for fields in this form in bulk.
29000      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29001      * @return {BasicForm} this
29002      */
29003     setValues : function(values){
29004         if(values instanceof Array){ // array of objects
29005             for(var i = 0, len = values.length; i < len; i++){
29006                 var v = values[i];
29007                 var f = this.findField(v.id);
29008                 if(f){
29009                     f.setValue(v.value);
29010                     if(this.trackResetOnLoad){
29011                         f.originalValue = f.getValue();
29012                     }
29013                 }
29014             }
29015         }else{ // object hash
29016             var field, id;
29017             for(id in values){
29018                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29019                     
29020                     if (field.setFromData && 
29021                         field.valueField && 
29022                         field.displayField &&
29023                         // combos' with local stores can 
29024                         // be queried via setValue()
29025                         // to set their value..
29026                         (field.store && !field.store.isLocal)
29027                         ) {
29028                         // it's a combo
29029                         var sd = { };
29030                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29031                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29032                         field.setFromData(sd);
29033                         
29034                     } else {
29035                         field.setValue(values[id]);
29036                     }
29037                     
29038                     
29039                     if(this.trackResetOnLoad){
29040                         field.originalValue = field.getValue();
29041                     }
29042                 }
29043             }
29044         }
29045         this.resetHasChanged();
29046         
29047         
29048         Roo.each(this.childForms || [], function (f) {
29049             f.setValues(values);
29050             f.resetHasChanged();
29051         });
29052                 
29053         return this;
29054     },
29055
29056     /**
29057      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29058      * they are returned as an array.
29059      * @param {Boolean} asString
29060      * @return {Object}
29061      */
29062     getValues : function(asString){
29063         if (this.childForms) {
29064             // copy values from the child forms
29065             Roo.each(this.childForms, function (f) {
29066                 this.setValues(f.getValues());
29067             }, this);
29068         }
29069         
29070         
29071         
29072         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29073         if(asString === true){
29074             return fs;
29075         }
29076         return Roo.urlDecode(fs);
29077     },
29078     
29079     /**
29080      * Returns the fields in this form as an object with key/value pairs. 
29081      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29082      * @return {Object}
29083      */
29084     getFieldValues : function(with_hidden)
29085     {
29086         if (this.childForms) {
29087             // copy values from the child forms
29088             // should this call getFieldValues - probably not as we do not currently copy
29089             // hidden fields when we generate..
29090             Roo.each(this.childForms, function (f) {
29091                 this.setValues(f.getValues());
29092             }, this);
29093         }
29094         
29095         var ret = {};
29096         this.items.each(function(f){
29097             if (!f.getName()) {
29098                 return;
29099             }
29100             var v = f.getValue();
29101             if (f.inputType =='radio') {
29102                 if (typeof(ret[f.getName()]) == 'undefined') {
29103                     ret[f.getName()] = ''; // empty..
29104                 }
29105                 
29106                 if (!f.el.dom.checked) {
29107                     return;
29108                     
29109                 }
29110                 v = f.el.dom.value;
29111                 
29112             }
29113             
29114             // not sure if this supported any more..
29115             if ((typeof(v) == 'object') && f.getRawValue) {
29116                 v = f.getRawValue() ; // dates..
29117             }
29118             // combo boxes where name != hiddenName...
29119             if (f.name != f.getName()) {
29120                 ret[f.name] = f.getRawValue();
29121             }
29122             ret[f.getName()] = v;
29123         });
29124         
29125         return ret;
29126     },
29127
29128     /**
29129      * Clears all invalid messages in this form.
29130      * @return {BasicForm} this
29131      */
29132     clearInvalid : function(){
29133         this.items.each(function(f){
29134            f.clearInvalid();
29135         });
29136         
29137         Roo.each(this.childForms || [], function (f) {
29138             f.clearInvalid();
29139         });
29140         
29141         
29142         return this;
29143     },
29144
29145     /**
29146      * Resets this form.
29147      * @return {BasicForm} this
29148      */
29149     reset : function(){
29150         this.items.each(function(f){
29151             f.reset();
29152         });
29153         
29154         Roo.each(this.childForms || [], function (f) {
29155             f.reset();
29156         });
29157        
29158         
29159         return this;
29160     },
29161
29162     /**
29163      * Add Roo.form components to this form.
29164      * @param {Field} field1
29165      * @param {Field} field2 (optional)
29166      * @param {Field} etc (optional)
29167      * @return {BasicForm} this
29168      */
29169     add : function(){
29170         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29171         return this;
29172     },
29173
29174
29175     /**
29176      * Removes a field from the items collection (does NOT remove its markup).
29177      * @param {Field} field
29178      * @return {BasicForm} this
29179      */
29180     remove : function(field){
29181         this.items.remove(field);
29182         return this;
29183     },
29184
29185     /**
29186      * Looks at the fields in this form, checks them for an id attribute,
29187      * and calls applyTo on the existing dom element with that id.
29188      * @return {BasicForm} this
29189      */
29190     render : function(){
29191         this.items.each(function(f){
29192             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29193                 f.applyTo(f.id);
29194             }
29195         });
29196         return this;
29197     },
29198
29199     /**
29200      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29201      * @param {Object} values
29202      * @return {BasicForm} this
29203      */
29204     applyToFields : function(o){
29205         this.items.each(function(f){
29206            Roo.apply(f, o);
29207         });
29208         return this;
29209     },
29210
29211     /**
29212      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29213      * @param {Object} values
29214      * @return {BasicForm} this
29215      */
29216     applyIfToFields : function(o){
29217         this.items.each(function(f){
29218            Roo.applyIf(f, o);
29219         });
29220         return this;
29221     }
29222 });
29223
29224 // back compat
29225 Roo.BasicForm = Roo.form.BasicForm;/*
29226  * Based on:
29227  * Ext JS Library 1.1.1
29228  * Copyright(c) 2006-2007, Ext JS, LLC.
29229  *
29230  * Originally Released Under LGPL - original licence link has changed is not relivant.
29231  *
29232  * Fork - LGPL
29233  * <script type="text/javascript">
29234  */
29235
29236 /**
29237  * @class Roo.form.Form
29238  * @extends Roo.form.BasicForm
29239  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29240  * @constructor
29241  * @param {Object} config Configuration options
29242  */
29243 Roo.form.Form = function(config){
29244     var xitems =  [];
29245     if (config.items) {
29246         xitems = config.items;
29247         delete config.items;
29248     }
29249    
29250     
29251     Roo.form.Form.superclass.constructor.call(this, null, config);
29252     this.url = this.url || this.action;
29253     if(!this.root){
29254         this.root = new Roo.form.Layout(Roo.applyIf({
29255             id: Roo.id()
29256         }, config));
29257     }
29258     this.active = this.root;
29259     /**
29260      * Array of all the buttons that have been added to this form via {@link addButton}
29261      * @type Array
29262      */
29263     this.buttons = [];
29264     this.allItems = [];
29265     this.addEvents({
29266         /**
29267          * @event clientvalidation
29268          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29269          * @param {Form} this
29270          * @param {Boolean} valid true if the form has passed client-side validation
29271          */
29272         clientvalidation: true,
29273         /**
29274          * @event rendered
29275          * Fires when the form is rendered
29276          * @param {Roo.form.Form} form
29277          */
29278         rendered : true
29279     });
29280     
29281     if (this.progressUrl) {
29282             // push a hidden field onto the list of fields..
29283             this.addxtype( {
29284                     xns: Roo.form, 
29285                     xtype : 'Hidden', 
29286                     name : 'UPLOAD_IDENTIFIER' 
29287             });
29288         }
29289         
29290     
29291     Roo.each(xitems, this.addxtype, this);
29292     
29293     
29294     
29295 };
29296
29297 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29298     /**
29299      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29300      */
29301     /**
29302      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29303      */
29304     /**
29305      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29306      */
29307     buttonAlign:'center',
29308
29309     /**
29310      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29311      */
29312     minButtonWidth:75,
29313
29314     /**
29315      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29316      * This property cascades to child containers if not set.
29317      */
29318     labelAlign:'left',
29319
29320     /**
29321      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29322      * fires a looping event with that state. This is required to bind buttons to the valid
29323      * state using the config value formBind:true on the button.
29324      */
29325     monitorValid : false,
29326
29327     /**
29328      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29329      */
29330     monitorPoll : 200,
29331     
29332     /**
29333      * @cfg {String} progressUrl - Url to return progress data 
29334      */
29335     
29336     progressUrl : false,
29337   
29338     /**
29339      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29340      * fields are added and the column is closed. If no fields are passed the column remains open
29341      * until end() is called.
29342      * @param {Object} config The config to pass to the column
29343      * @param {Field} field1 (optional)
29344      * @param {Field} field2 (optional)
29345      * @param {Field} etc (optional)
29346      * @return Column The column container object
29347      */
29348     column : function(c){
29349         var col = new Roo.form.Column(c);
29350         this.start(col);
29351         if(arguments.length > 1){ // duplicate code required because of Opera
29352             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29353             this.end();
29354         }
29355         return col;
29356     },
29357
29358     /**
29359      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29360      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29361      * until end() is called.
29362      * @param {Object} config The config to pass to the fieldset
29363      * @param {Field} field1 (optional)
29364      * @param {Field} field2 (optional)
29365      * @param {Field} etc (optional)
29366      * @return FieldSet The fieldset container object
29367      */
29368     fieldset : function(c){
29369         var fs = new Roo.form.FieldSet(c);
29370         this.start(fs);
29371         if(arguments.length > 1){ // duplicate code required because of Opera
29372             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29373             this.end();
29374         }
29375         return fs;
29376     },
29377
29378     /**
29379      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29380      * fields are added and the container is closed. If no fields are passed the container remains open
29381      * until end() is called.
29382      * @param {Object} config The config to pass to the Layout
29383      * @param {Field} field1 (optional)
29384      * @param {Field} field2 (optional)
29385      * @param {Field} etc (optional)
29386      * @return Layout The container object
29387      */
29388     container : function(c){
29389         var l = new Roo.form.Layout(c);
29390         this.start(l);
29391         if(arguments.length > 1){ // duplicate code required because of Opera
29392             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29393             this.end();
29394         }
29395         return l;
29396     },
29397
29398     /**
29399      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29400      * @param {Object} container A Roo.form.Layout or subclass of Layout
29401      * @return {Form} this
29402      */
29403     start : function(c){
29404         // cascade label info
29405         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29406         this.active.stack.push(c);
29407         c.ownerCt = this.active;
29408         this.active = c;
29409         return this;
29410     },
29411
29412     /**
29413      * Closes the current open container
29414      * @return {Form} this
29415      */
29416     end : function(){
29417         if(this.active == this.root){
29418             return this;
29419         }
29420         this.active = this.active.ownerCt;
29421         return this;
29422     },
29423
29424     /**
29425      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29426      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29427      * as the label of the field.
29428      * @param {Field} field1
29429      * @param {Field} field2 (optional)
29430      * @param {Field} etc. (optional)
29431      * @return {Form} this
29432      */
29433     add : function(){
29434         this.active.stack.push.apply(this.active.stack, arguments);
29435         this.allItems.push.apply(this.allItems,arguments);
29436         var r = [];
29437         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29438             if(a[i].isFormField){
29439                 r.push(a[i]);
29440             }
29441         }
29442         if(r.length > 0){
29443             Roo.form.Form.superclass.add.apply(this, r);
29444         }
29445         return this;
29446     },
29447     
29448
29449     
29450     
29451     
29452      /**
29453      * Find any element that has been added to a form, using it's ID or name
29454      * This can include framesets, columns etc. along with regular fields..
29455      * @param {String} id - id or name to find.
29456      
29457      * @return {Element} e - or false if nothing found.
29458      */
29459     findbyId : function(id)
29460     {
29461         var ret = false;
29462         if (!id) {
29463             return ret;
29464         }
29465         Roo.each(this.allItems, function(f){
29466             if (f.id == id || f.name == id ){
29467                 ret = f;
29468                 return false;
29469             }
29470         });
29471         return ret;
29472     },
29473
29474     
29475     
29476     /**
29477      * Render this form into the passed container. This should only be called once!
29478      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29479      * @return {Form} this
29480      */
29481     render : function(ct)
29482     {
29483         
29484         
29485         
29486         ct = Roo.get(ct);
29487         var o = this.autoCreate || {
29488             tag: 'form',
29489             method : this.method || 'POST',
29490             id : this.id || Roo.id()
29491         };
29492         this.initEl(ct.createChild(o));
29493
29494         this.root.render(this.el);
29495         
29496        
29497              
29498         this.items.each(function(f){
29499             f.render('x-form-el-'+f.id);
29500         });
29501
29502         if(this.buttons.length > 0){
29503             // tables are required to maintain order and for correct IE layout
29504             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29505                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29506                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29507             }}, null, true);
29508             var tr = tb.getElementsByTagName('tr')[0];
29509             for(var i = 0, len = this.buttons.length; i < len; i++) {
29510                 var b = this.buttons[i];
29511                 var td = document.createElement('td');
29512                 td.className = 'x-form-btn-td';
29513                 b.render(tr.appendChild(td));
29514             }
29515         }
29516         if(this.monitorValid){ // initialize after render
29517             this.startMonitoring();
29518         }
29519         this.fireEvent('rendered', this);
29520         return this;
29521     },
29522
29523     /**
29524      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29525      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29526      * object or a valid Roo.DomHelper element config
29527      * @param {Function} handler The function called when the button is clicked
29528      * @param {Object} scope (optional) The scope of the handler function
29529      * @return {Roo.Button}
29530      */
29531     addButton : function(config, handler, scope){
29532         var bc = {
29533             handler: handler,
29534             scope: scope,
29535             minWidth: this.minButtonWidth,
29536             hideParent:true
29537         };
29538         if(typeof config == "string"){
29539             bc.text = config;
29540         }else{
29541             Roo.apply(bc, config);
29542         }
29543         var btn = new Roo.Button(null, bc);
29544         this.buttons.push(btn);
29545         return btn;
29546     },
29547
29548      /**
29549      * Adds a series of form elements (using the xtype property as the factory method.
29550      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29551      * @param {Object} config 
29552      */
29553     
29554     addxtype : function()
29555     {
29556         var ar = Array.prototype.slice.call(arguments, 0);
29557         var ret = false;
29558         for(var i = 0; i < ar.length; i++) {
29559             if (!ar[i]) {
29560                 continue; // skip -- if this happends something invalid got sent, we 
29561                 // should ignore it, as basically that interface element will not show up
29562                 // and that should be pretty obvious!!
29563             }
29564             
29565             if (Roo.form[ar[i].xtype]) {
29566                 ar[i].form = this;
29567                 var fe = Roo.factory(ar[i], Roo.form);
29568                 if (!ret) {
29569                     ret = fe;
29570                 }
29571                 fe.form = this;
29572                 if (fe.store) {
29573                     fe.store.form = this;
29574                 }
29575                 if (fe.isLayout) {  
29576                          
29577                     this.start(fe);
29578                     this.allItems.push(fe);
29579                     if (fe.items && fe.addxtype) {
29580                         fe.addxtype.apply(fe, fe.items);
29581                         delete fe.items;
29582                     }
29583                      this.end();
29584                     continue;
29585                 }
29586                 
29587                 
29588                  
29589                 this.add(fe);
29590               //  console.log('adding ' + ar[i].xtype);
29591             }
29592             if (ar[i].xtype == 'Button') {  
29593                 //console.log('adding button');
29594                 //console.log(ar[i]);
29595                 this.addButton(ar[i]);
29596                 this.allItems.push(fe);
29597                 continue;
29598             }
29599             
29600             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29601                 alert('end is not supported on xtype any more, use items');
29602             //    this.end();
29603             //    //console.log('adding end');
29604             }
29605             
29606         }
29607         return ret;
29608     },
29609     
29610     /**
29611      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29612      * option "monitorValid"
29613      */
29614     startMonitoring : function(){
29615         if(!this.bound){
29616             this.bound = true;
29617             Roo.TaskMgr.start({
29618                 run : this.bindHandler,
29619                 interval : this.monitorPoll || 200,
29620                 scope: this
29621             });
29622         }
29623     },
29624
29625     /**
29626      * Stops monitoring of the valid state of this form
29627      */
29628     stopMonitoring : function(){
29629         this.bound = false;
29630     },
29631
29632     // private
29633     bindHandler : function(){
29634         if(!this.bound){
29635             return false; // stops binding
29636         }
29637         var valid = true;
29638         this.items.each(function(f){
29639             if(!f.isValid(true)){
29640                 valid = false;
29641                 return false;
29642             }
29643         });
29644         for(var i = 0, len = this.buttons.length; i < len; i++){
29645             var btn = this.buttons[i];
29646             if(btn.formBind === true && btn.disabled === valid){
29647                 btn.setDisabled(!valid);
29648             }
29649         }
29650         this.fireEvent('clientvalidation', this, valid);
29651     }
29652     
29653     
29654     
29655     
29656     
29657     
29658     
29659     
29660 });
29661
29662
29663 // back compat
29664 Roo.Form = Roo.form.Form;
29665 /*
29666  * Based on:
29667  * Ext JS Library 1.1.1
29668  * Copyright(c) 2006-2007, Ext JS, LLC.
29669  *
29670  * Originally Released Under LGPL - original licence link has changed is not relivant.
29671  *
29672  * Fork - LGPL
29673  * <script type="text/javascript">
29674  */
29675
29676 // as we use this in bootstrap.
29677 Roo.namespace('Roo.form');
29678  /**
29679  * @class Roo.form.Action
29680  * Internal Class used to handle form actions
29681  * @constructor
29682  * @param {Roo.form.BasicForm} el The form element or its id
29683  * @param {Object} config Configuration options
29684  */
29685
29686  
29687  
29688 // define the action interface
29689 Roo.form.Action = function(form, options){
29690     this.form = form;
29691     this.options = options || {};
29692 };
29693 /**
29694  * Client Validation Failed
29695  * @const 
29696  */
29697 Roo.form.Action.CLIENT_INVALID = 'client';
29698 /**
29699  * Server Validation Failed
29700  * @const 
29701  */
29702 Roo.form.Action.SERVER_INVALID = 'server';
29703  /**
29704  * Connect to Server Failed
29705  * @const 
29706  */
29707 Roo.form.Action.CONNECT_FAILURE = 'connect';
29708 /**
29709  * Reading Data from Server Failed
29710  * @const 
29711  */
29712 Roo.form.Action.LOAD_FAILURE = 'load';
29713
29714 Roo.form.Action.prototype = {
29715     type : 'default',
29716     failureType : undefined,
29717     response : undefined,
29718     result : undefined,
29719
29720     // interface method
29721     run : function(options){
29722
29723     },
29724
29725     // interface method
29726     success : function(response){
29727
29728     },
29729
29730     // interface method
29731     handleResponse : function(response){
29732
29733     },
29734
29735     // default connection failure
29736     failure : function(response){
29737         
29738         this.response = response;
29739         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29740         this.form.afterAction(this, false);
29741     },
29742
29743     processResponse : function(response){
29744         this.response = response;
29745         if(!response.responseText){
29746             return true;
29747         }
29748         this.result = this.handleResponse(response);
29749         return this.result;
29750     },
29751
29752     // utility functions used internally
29753     getUrl : function(appendParams){
29754         var url = this.options.url || this.form.url || this.form.el.dom.action;
29755         if(appendParams){
29756             var p = this.getParams();
29757             if(p){
29758                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29759             }
29760         }
29761         return url;
29762     },
29763
29764     getMethod : function(){
29765         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29766     },
29767
29768     getParams : function(){
29769         var bp = this.form.baseParams;
29770         var p = this.options.params;
29771         if(p){
29772             if(typeof p == "object"){
29773                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29774             }else if(typeof p == 'string' && bp){
29775                 p += '&' + Roo.urlEncode(bp);
29776             }
29777         }else if(bp){
29778             p = Roo.urlEncode(bp);
29779         }
29780         return p;
29781     },
29782
29783     createCallback : function(){
29784         return {
29785             success: this.success,
29786             failure: this.failure,
29787             scope: this,
29788             timeout: (this.form.timeout*1000),
29789             upload: this.form.fileUpload ? this.success : undefined
29790         };
29791     }
29792 };
29793
29794 Roo.form.Action.Submit = function(form, options){
29795     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29796 };
29797
29798 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29799     type : 'submit',
29800
29801     haveProgress : false,
29802     uploadComplete : false,
29803     
29804     // uploadProgress indicator.
29805     uploadProgress : function()
29806     {
29807         if (!this.form.progressUrl) {
29808             return;
29809         }
29810         
29811         if (!this.haveProgress) {
29812             Roo.MessageBox.progress("Uploading", "Uploading");
29813         }
29814         if (this.uploadComplete) {
29815            Roo.MessageBox.hide();
29816            return;
29817         }
29818         
29819         this.haveProgress = true;
29820    
29821         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29822         
29823         var c = new Roo.data.Connection();
29824         c.request({
29825             url : this.form.progressUrl,
29826             params: {
29827                 id : uid
29828             },
29829             method: 'GET',
29830             success : function(req){
29831                //console.log(data);
29832                 var rdata = false;
29833                 var edata;
29834                 try  {
29835                    rdata = Roo.decode(req.responseText)
29836                 } catch (e) {
29837                     Roo.log("Invalid data from server..");
29838                     Roo.log(edata);
29839                     return;
29840                 }
29841                 if (!rdata || !rdata.success) {
29842                     Roo.log(rdata);
29843                     Roo.MessageBox.alert(Roo.encode(rdata));
29844                     return;
29845                 }
29846                 var data = rdata.data;
29847                 
29848                 if (this.uploadComplete) {
29849                    Roo.MessageBox.hide();
29850                    return;
29851                 }
29852                    
29853                 if (data){
29854                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29855                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29856                     );
29857                 }
29858                 this.uploadProgress.defer(2000,this);
29859             },
29860        
29861             failure: function(data) {
29862                 Roo.log('progress url failed ');
29863                 Roo.log(data);
29864             },
29865             scope : this
29866         });
29867            
29868     },
29869     
29870     
29871     run : function()
29872     {
29873         // run get Values on the form, so it syncs any secondary forms.
29874         this.form.getValues();
29875         
29876         var o = this.options;
29877         var method = this.getMethod();
29878         var isPost = method == 'POST';
29879         if(o.clientValidation === false || this.form.isValid()){
29880             
29881             if (this.form.progressUrl) {
29882                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29883                     (new Date() * 1) + '' + Math.random());
29884                     
29885             } 
29886             
29887             
29888             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29889                 form:this.form.el.dom,
29890                 url:this.getUrl(!isPost),
29891                 method: method,
29892                 params:isPost ? this.getParams() : null,
29893                 isUpload: this.form.fileUpload
29894             }));
29895             
29896             this.uploadProgress();
29897
29898         }else if (o.clientValidation !== false){ // client validation failed
29899             this.failureType = Roo.form.Action.CLIENT_INVALID;
29900             this.form.afterAction(this, false);
29901         }
29902     },
29903
29904     success : function(response)
29905     {
29906         this.uploadComplete= true;
29907         if (this.haveProgress) {
29908             Roo.MessageBox.hide();
29909         }
29910         
29911         
29912         var result = this.processResponse(response);
29913         if(result === true || result.success){
29914             this.form.afterAction(this, true);
29915             return;
29916         }
29917         if(result.errors){
29918             this.form.markInvalid(result.errors);
29919             this.failureType = Roo.form.Action.SERVER_INVALID;
29920         }
29921         this.form.afterAction(this, false);
29922     },
29923     failure : function(response)
29924     {
29925         this.uploadComplete= true;
29926         if (this.haveProgress) {
29927             Roo.MessageBox.hide();
29928         }
29929         
29930         this.response = response;
29931         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29932         this.form.afterAction(this, false);
29933     },
29934     
29935     handleResponse : function(response){
29936         if(this.form.errorReader){
29937             var rs = this.form.errorReader.read(response);
29938             var errors = [];
29939             if(rs.records){
29940                 for(var i = 0, len = rs.records.length; i < len; i++) {
29941                     var r = rs.records[i];
29942                     errors[i] = r.data;
29943                 }
29944             }
29945             if(errors.length < 1){
29946                 errors = null;
29947             }
29948             return {
29949                 success : rs.success,
29950                 errors : errors
29951             };
29952         }
29953         var ret = false;
29954         try {
29955             ret = Roo.decode(response.responseText);
29956         } catch (e) {
29957             ret = {
29958                 success: false,
29959                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29960                 errors : []
29961             };
29962         }
29963         return ret;
29964         
29965     }
29966 });
29967
29968
29969 Roo.form.Action.Load = function(form, options){
29970     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29971     this.reader = this.form.reader;
29972 };
29973
29974 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29975     type : 'load',
29976
29977     run : function(){
29978         
29979         Roo.Ajax.request(Roo.apply(
29980                 this.createCallback(), {
29981                     method:this.getMethod(),
29982                     url:this.getUrl(false),
29983                     params:this.getParams()
29984         }));
29985     },
29986
29987     success : function(response){
29988         
29989         var result = this.processResponse(response);
29990         if(result === true || !result.success || !result.data){
29991             this.failureType = Roo.form.Action.LOAD_FAILURE;
29992             this.form.afterAction(this, false);
29993             return;
29994         }
29995         this.form.clearInvalid();
29996         this.form.setValues(result.data);
29997         this.form.afterAction(this, true);
29998     },
29999
30000     handleResponse : function(response){
30001         if(this.form.reader){
30002             var rs = this.form.reader.read(response);
30003             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30004             return {
30005                 success : rs.success,
30006                 data : data
30007             };
30008         }
30009         return Roo.decode(response.responseText);
30010     }
30011 });
30012
30013 Roo.form.Action.ACTION_TYPES = {
30014     'load' : Roo.form.Action.Load,
30015     'submit' : Roo.form.Action.Submit
30016 };/*
30017  * Based on:
30018  * Ext JS Library 1.1.1
30019  * Copyright(c) 2006-2007, Ext JS, LLC.
30020  *
30021  * Originally Released Under LGPL - original licence link has changed is not relivant.
30022  *
30023  * Fork - LGPL
30024  * <script type="text/javascript">
30025  */
30026  
30027 /**
30028  * @class Roo.form.Layout
30029  * @extends Roo.Component
30030  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30031  * @constructor
30032  * @param {Object} config Configuration options
30033  */
30034 Roo.form.Layout = function(config){
30035     var xitems = [];
30036     if (config.items) {
30037         xitems = config.items;
30038         delete config.items;
30039     }
30040     Roo.form.Layout.superclass.constructor.call(this, config);
30041     this.stack = [];
30042     Roo.each(xitems, this.addxtype, this);
30043      
30044 };
30045
30046 Roo.extend(Roo.form.Layout, Roo.Component, {
30047     /**
30048      * @cfg {String/Object} autoCreate
30049      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30050      */
30051     /**
30052      * @cfg {String/Object/Function} style
30053      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30054      * a function which returns such a specification.
30055      */
30056     /**
30057      * @cfg {String} labelAlign
30058      * Valid values are "left," "top" and "right" (defaults to "left")
30059      */
30060     /**
30061      * @cfg {Number} labelWidth
30062      * Fixed width in pixels of all field labels (defaults to undefined)
30063      */
30064     /**
30065      * @cfg {Boolean} clear
30066      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30067      */
30068     clear : true,
30069     /**
30070      * @cfg {String} labelSeparator
30071      * The separator to use after field labels (defaults to ':')
30072      */
30073     labelSeparator : ':',
30074     /**
30075      * @cfg {Boolean} hideLabels
30076      * True to suppress the display of field labels in this layout (defaults to false)
30077      */
30078     hideLabels : false,
30079
30080     // private
30081     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30082     
30083     isLayout : true,
30084     
30085     // private
30086     onRender : function(ct, position){
30087         if(this.el){ // from markup
30088             this.el = Roo.get(this.el);
30089         }else {  // generate
30090             var cfg = this.getAutoCreate();
30091             this.el = ct.createChild(cfg, position);
30092         }
30093         if(this.style){
30094             this.el.applyStyles(this.style);
30095         }
30096         if(this.labelAlign){
30097             this.el.addClass('x-form-label-'+this.labelAlign);
30098         }
30099         if(this.hideLabels){
30100             this.labelStyle = "display:none";
30101             this.elementStyle = "padding-left:0;";
30102         }else{
30103             if(typeof this.labelWidth == 'number'){
30104                 this.labelStyle = "width:"+this.labelWidth+"px;";
30105                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30106             }
30107             if(this.labelAlign == 'top'){
30108                 this.labelStyle = "width:auto;";
30109                 this.elementStyle = "padding-left:0;";
30110             }
30111         }
30112         var stack = this.stack;
30113         var slen = stack.length;
30114         if(slen > 0){
30115             if(!this.fieldTpl){
30116                 var t = new Roo.Template(
30117                     '<div class="x-form-item {5}">',
30118                         '<label for="{0}" style="{2}">{1}{4}</label>',
30119                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30120                         '</div>',
30121                     '</div><div class="x-form-clear-left"></div>'
30122                 );
30123                 t.disableFormats = true;
30124                 t.compile();
30125                 Roo.form.Layout.prototype.fieldTpl = t;
30126             }
30127             for(var i = 0; i < slen; i++) {
30128                 if(stack[i].isFormField){
30129                     this.renderField(stack[i]);
30130                 }else{
30131                     this.renderComponent(stack[i]);
30132                 }
30133             }
30134         }
30135         if(this.clear){
30136             this.el.createChild({cls:'x-form-clear'});
30137         }
30138     },
30139
30140     // private
30141     renderField : function(f){
30142         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30143                f.id, //0
30144                f.fieldLabel, //1
30145                f.labelStyle||this.labelStyle||'', //2
30146                this.elementStyle||'', //3
30147                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30148                f.itemCls||this.itemCls||''  //5
30149        ], true).getPrevSibling());
30150     },
30151
30152     // private
30153     renderComponent : function(c){
30154         c.render(c.isLayout ? this.el : this.el.createChild());    
30155     },
30156     /**
30157      * Adds a object form elements (using the xtype property as the factory method.)
30158      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30159      * @param {Object} config 
30160      */
30161     addxtype : function(o)
30162     {
30163         // create the lement.
30164         o.form = this.form;
30165         var fe = Roo.factory(o, Roo.form);
30166         this.form.allItems.push(fe);
30167         this.stack.push(fe);
30168         
30169         if (fe.isFormField) {
30170             this.form.items.add(fe);
30171         }
30172          
30173         return fe;
30174     }
30175 });
30176
30177 /**
30178  * @class Roo.form.Column
30179  * @extends Roo.form.Layout
30180  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30181  * @constructor
30182  * @param {Object} config Configuration options
30183  */
30184 Roo.form.Column = function(config){
30185     Roo.form.Column.superclass.constructor.call(this, config);
30186 };
30187
30188 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30189     /**
30190      * @cfg {Number/String} width
30191      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30192      */
30193     /**
30194      * @cfg {String/Object} autoCreate
30195      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30196      */
30197
30198     // private
30199     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30200
30201     // private
30202     onRender : function(ct, position){
30203         Roo.form.Column.superclass.onRender.call(this, ct, position);
30204         if(this.width){
30205             this.el.setWidth(this.width);
30206         }
30207     }
30208 });
30209
30210
30211 /**
30212  * @class Roo.form.Row
30213  * @extends Roo.form.Layout
30214  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30215  * @constructor
30216  * @param {Object} config Configuration options
30217  */
30218
30219  
30220 Roo.form.Row = function(config){
30221     Roo.form.Row.superclass.constructor.call(this, config);
30222 };
30223  
30224 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30225       /**
30226      * @cfg {Number/String} width
30227      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30228      */
30229     /**
30230      * @cfg {Number/String} height
30231      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30232      */
30233     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30234     
30235     padWidth : 20,
30236     // private
30237     onRender : function(ct, position){
30238         //console.log('row render');
30239         if(!this.rowTpl){
30240             var t = new Roo.Template(
30241                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30242                     '<label for="{0}" style="{2}">{1}{4}</label>',
30243                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30244                     '</div>',
30245                 '</div>'
30246             );
30247             t.disableFormats = true;
30248             t.compile();
30249             Roo.form.Layout.prototype.rowTpl = t;
30250         }
30251         this.fieldTpl = this.rowTpl;
30252         
30253         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30254         var labelWidth = 100;
30255         
30256         if ((this.labelAlign != 'top')) {
30257             if (typeof this.labelWidth == 'number') {
30258                 labelWidth = this.labelWidth
30259             }
30260             this.padWidth =  20 + labelWidth;
30261             
30262         }
30263         
30264         Roo.form.Column.superclass.onRender.call(this, ct, position);
30265         if(this.width){
30266             this.el.setWidth(this.width);
30267         }
30268         if(this.height){
30269             this.el.setHeight(this.height);
30270         }
30271     },
30272     
30273     // private
30274     renderField : function(f){
30275         f.fieldEl = this.fieldTpl.append(this.el, [
30276                f.id, f.fieldLabel,
30277                f.labelStyle||this.labelStyle||'',
30278                this.elementStyle||'',
30279                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30280                f.itemCls||this.itemCls||'',
30281                f.width ? f.width + this.padWidth : 160 + this.padWidth
30282        ],true);
30283     }
30284 });
30285  
30286
30287 /**
30288  * @class Roo.form.FieldSet
30289  * @extends Roo.form.Layout
30290  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30291  * @constructor
30292  * @param {Object} config Configuration options
30293  */
30294 Roo.form.FieldSet = function(config){
30295     Roo.form.FieldSet.superclass.constructor.call(this, config);
30296 };
30297
30298 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30299     /**
30300      * @cfg {String} legend
30301      * The text to display as the legend for the FieldSet (defaults to '')
30302      */
30303     /**
30304      * @cfg {String/Object} autoCreate
30305      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30306      */
30307
30308     // private
30309     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30310
30311     // private
30312     onRender : function(ct, position){
30313         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30314         if(this.legend){
30315             this.setLegend(this.legend);
30316         }
30317     },
30318
30319     // private
30320     setLegend : function(text){
30321         if(this.rendered){
30322             this.el.child('legend').update(text);
30323         }
30324     }
30325 });/*
30326  * Based on:
30327  * Ext JS Library 1.1.1
30328  * Copyright(c) 2006-2007, Ext JS, LLC.
30329  *
30330  * Originally Released Under LGPL - original licence link has changed is not relivant.
30331  *
30332  * Fork - LGPL
30333  * <script type="text/javascript">
30334  */
30335 /**
30336  * @class Roo.form.VTypes
30337  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30338  * @singleton
30339  */
30340 Roo.form.VTypes = function(){
30341     // closure these in so they are only created once.
30342     var alpha = /^[a-zA-Z_]+$/;
30343     var alphanum = /^[a-zA-Z0-9_]+$/;
30344     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30345     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30346
30347     // All these messages and functions are configurable
30348     return {
30349         /**
30350          * The function used to validate email addresses
30351          * @param {String} value The email address
30352          */
30353         'email' : function(v){
30354             return email.test(v);
30355         },
30356         /**
30357          * The error text to display when the email validation function returns false
30358          * @type String
30359          */
30360         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30361         /**
30362          * The keystroke filter mask to be applied on email input
30363          * @type RegExp
30364          */
30365         'emailMask' : /[a-z0-9_\.\-@]/i,
30366
30367         /**
30368          * The function used to validate URLs
30369          * @param {String} value The URL
30370          */
30371         'url' : function(v){
30372             return url.test(v);
30373         },
30374         /**
30375          * The error text to display when the url validation function returns false
30376          * @type String
30377          */
30378         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30379         
30380         /**
30381          * The function used to validate alpha values
30382          * @param {String} value The value
30383          */
30384         'alpha' : function(v){
30385             return alpha.test(v);
30386         },
30387         /**
30388          * The error text to display when the alpha validation function returns false
30389          * @type String
30390          */
30391         'alphaText' : 'This field should only contain letters and _',
30392         /**
30393          * The keystroke filter mask to be applied on alpha input
30394          * @type RegExp
30395          */
30396         'alphaMask' : /[a-z_]/i,
30397
30398         /**
30399          * The function used to validate alphanumeric values
30400          * @param {String} value The value
30401          */
30402         'alphanum' : function(v){
30403             return alphanum.test(v);
30404         },
30405         /**
30406          * The error text to display when the alphanumeric validation function returns false
30407          * @type String
30408          */
30409         'alphanumText' : 'This field should only contain letters, numbers and _',
30410         /**
30411          * The keystroke filter mask to be applied on alphanumeric input
30412          * @type RegExp
30413          */
30414         'alphanumMask' : /[a-z0-9_]/i
30415     };
30416 }();//<script type="text/javascript">
30417
30418 /**
30419  * @class Roo.form.FCKeditor
30420  * @extends Roo.form.TextArea
30421  * Wrapper around the FCKEditor http://www.fckeditor.net
30422  * @constructor
30423  * Creates a new FCKeditor
30424  * @param {Object} config Configuration options
30425  */
30426 Roo.form.FCKeditor = function(config){
30427     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30428     this.addEvents({
30429          /**
30430          * @event editorinit
30431          * Fired when the editor is initialized - you can add extra handlers here..
30432          * @param {FCKeditor} this
30433          * @param {Object} the FCK object.
30434          */
30435         editorinit : true
30436     });
30437     
30438     
30439 };
30440 Roo.form.FCKeditor.editors = { };
30441 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30442 {
30443     //defaultAutoCreate : {
30444     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30445     //},
30446     // private
30447     /**
30448      * @cfg {Object} fck options - see fck manual for details.
30449      */
30450     fckconfig : false,
30451     
30452     /**
30453      * @cfg {Object} fck toolbar set (Basic or Default)
30454      */
30455     toolbarSet : 'Basic',
30456     /**
30457      * @cfg {Object} fck BasePath
30458      */ 
30459     basePath : '/fckeditor/',
30460     
30461     
30462     frame : false,
30463     
30464     value : '',
30465     
30466    
30467     onRender : function(ct, position)
30468     {
30469         if(!this.el){
30470             this.defaultAutoCreate = {
30471                 tag: "textarea",
30472                 style:"width:300px;height:60px;",
30473                 autocomplete: "new-password"
30474             };
30475         }
30476         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30477         /*
30478         if(this.grow){
30479             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30480             if(this.preventScrollbars){
30481                 this.el.setStyle("overflow", "hidden");
30482             }
30483             this.el.setHeight(this.growMin);
30484         }
30485         */
30486         //console.log('onrender' + this.getId() );
30487         Roo.form.FCKeditor.editors[this.getId()] = this;
30488          
30489
30490         this.replaceTextarea() ;
30491         
30492     },
30493     
30494     getEditor : function() {
30495         return this.fckEditor;
30496     },
30497     /**
30498      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30499      * @param {Mixed} value The value to set
30500      */
30501     
30502     
30503     setValue : function(value)
30504     {
30505         //console.log('setValue: ' + value);
30506         
30507         if(typeof(value) == 'undefined') { // not sure why this is happending...
30508             return;
30509         }
30510         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30511         
30512         //if(!this.el || !this.getEditor()) {
30513         //    this.value = value;
30514             //this.setValue.defer(100,this,[value]);    
30515         //    return;
30516         //} 
30517         
30518         if(!this.getEditor()) {
30519             return;
30520         }
30521         
30522         this.getEditor().SetData(value);
30523         
30524         //
30525
30526     },
30527
30528     /**
30529      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30530      * @return {Mixed} value The field value
30531      */
30532     getValue : function()
30533     {
30534         
30535         if (this.frame && this.frame.dom.style.display == 'none') {
30536             return Roo.form.FCKeditor.superclass.getValue.call(this);
30537         }
30538         
30539         if(!this.el || !this.getEditor()) {
30540            
30541            // this.getValue.defer(100,this); 
30542             return this.value;
30543         }
30544        
30545         
30546         var value=this.getEditor().GetData();
30547         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30548         return Roo.form.FCKeditor.superclass.getValue.call(this);
30549         
30550
30551     },
30552
30553     /**
30554      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30555      * @return {Mixed} value The field value
30556      */
30557     getRawValue : function()
30558     {
30559         if (this.frame && this.frame.dom.style.display == 'none') {
30560             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30561         }
30562         
30563         if(!this.el || !this.getEditor()) {
30564             //this.getRawValue.defer(100,this); 
30565             return this.value;
30566             return;
30567         }
30568         
30569         
30570         
30571         var value=this.getEditor().GetData();
30572         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30573         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30574          
30575     },
30576     
30577     setSize : function(w,h) {
30578         
30579         
30580         
30581         //if (this.frame && this.frame.dom.style.display == 'none') {
30582         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30583         //    return;
30584         //}
30585         //if(!this.el || !this.getEditor()) {
30586         //    this.setSize.defer(100,this, [w,h]); 
30587         //    return;
30588         //}
30589         
30590         
30591         
30592         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30593         
30594         this.frame.dom.setAttribute('width', w);
30595         this.frame.dom.setAttribute('height', h);
30596         this.frame.setSize(w,h);
30597         
30598     },
30599     
30600     toggleSourceEdit : function(value) {
30601         
30602       
30603          
30604         this.el.dom.style.display = value ? '' : 'none';
30605         this.frame.dom.style.display = value ?  'none' : '';
30606         
30607     },
30608     
30609     
30610     focus: function(tag)
30611     {
30612         if (this.frame.dom.style.display == 'none') {
30613             return Roo.form.FCKeditor.superclass.focus.call(this);
30614         }
30615         if(!this.el || !this.getEditor()) {
30616             this.focus.defer(100,this, [tag]); 
30617             return;
30618         }
30619         
30620         
30621         
30622         
30623         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30624         this.getEditor().Focus();
30625         if (tgs.length) {
30626             if (!this.getEditor().Selection.GetSelection()) {
30627                 this.focus.defer(100,this, [tag]); 
30628                 return;
30629             }
30630             
30631             
30632             var r = this.getEditor().EditorDocument.createRange();
30633             r.setStart(tgs[0],0);
30634             r.setEnd(tgs[0],0);
30635             this.getEditor().Selection.GetSelection().removeAllRanges();
30636             this.getEditor().Selection.GetSelection().addRange(r);
30637             this.getEditor().Focus();
30638         }
30639         
30640     },
30641     
30642     
30643     
30644     replaceTextarea : function()
30645     {
30646         if ( document.getElementById( this.getId() + '___Frame' ) ) {
30647             return ;
30648         }
30649         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30650         //{
30651             // We must check the elements firstly using the Id and then the name.
30652         var oTextarea = document.getElementById( this.getId() );
30653         
30654         var colElementsByName = document.getElementsByName( this.getId() ) ;
30655          
30656         oTextarea.style.display = 'none' ;
30657
30658         if ( oTextarea.tabIndex ) {            
30659             this.TabIndex = oTextarea.tabIndex ;
30660         }
30661         
30662         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30663         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30664         this.frame = Roo.get(this.getId() + '___Frame')
30665     },
30666     
30667     _getConfigHtml : function()
30668     {
30669         var sConfig = '' ;
30670
30671         for ( var o in this.fckconfig ) {
30672             sConfig += sConfig.length > 0  ? '&amp;' : '';
30673             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30674         }
30675
30676         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30677     },
30678     
30679     
30680     _getIFrameHtml : function()
30681     {
30682         var sFile = 'fckeditor.html' ;
30683         /* no idea what this is about..
30684         try
30685         {
30686             if ( (/fcksource=true/i).test( window.top.location.search ) )
30687                 sFile = 'fckeditor.original.html' ;
30688         }
30689         catch (e) { 
30690         */
30691
30692         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30693         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30694         
30695         
30696         var html = '<iframe id="' + this.getId() +
30697             '___Frame" src="' + sLink +
30698             '" width="' + this.width +
30699             '" height="' + this.height + '"' +
30700             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30701             ' frameborder="0" scrolling="no"></iframe>' ;
30702
30703         return html ;
30704     },
30705     
30706     _insertHtmlBefore : function( html, element )
30707     {
30708         if ( element.insertAdjacentHTML )       {
30709             // IE
30710             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30711         } else { // Gecko
30712             var oRange = document.createRange() ;
30713             oRange.setStartBefore( element ) ;
30714             var oFragment = oRange.createContextualFragment( html );
30715             element.parentNode.insertBefore( oFragment, element ) ;
30716         }
30717     }
30718     
30719     
30720   
30721     
30722     
30723     
30724     
30725
30726 });
30727
30728 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30729
30730 function FCKeditor_OnComplete(editorInstance){
30731     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30732     f.fckEditor = editorInstance;
30733     //console.log("loaded");
30734     f.fireEvent('editorinit', f, editorInstance);
30735
30736   
30737
30738  
30739
30740
30741
30742
30743
30744
30745
30746
30747
30748
30749
30750
30751
30752
30753
30754 //<script type="text/javascript">
30755 /**
30756  * @class Roo.form.GridField
30757  * @extends Roo.form.Field
30758  * Embed a grid (or editable grid into a form)
30759  * STATUS ALPHA
30760  * 
30761  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30762  * it needs 
30763  * xgrid.store = Roo.data.Store
30764  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30765  * xgrid.store.reader = Roo.data.JsonReader 
30766  * 
30767  * 
30768  * @constructor
30769  * Creates a new GridField
30770  * @param {Object} config Configuration options
30771  */
30772 Roo.form.GridField = function(config){
30773     Roo.form.GridField.superclass.constructor.call(this, config);
30774      
30775 };
30776
30777 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30778     /**
30779      * @cfg {Number} width  - used to restrict width of grid..
30780      */
30781     width : 100,
30782     /**
30783      * @cfg {Number} height - used to restrict height of grid..
30784      */
30785     height : 50,
30786      /**
30787      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30788          * 
30789          *}
30790      */
30791     xgrid : false, 
30792     /**
30793      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30794      * {tag: "input", type: "checkbox", autocomplete: "off"})
30795      */
30796    // defaultAutoCreate : { tag: 'div' },
30797     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30798     /**
30799      * @cfg {String} addTitle Text to include for adding a title.
30800      */
30801     addTitle : false,
30802     //
30803     onResize : function(){
30804         Roo.form.Field.superclass.onResize.apply(this, arguments);
30805     },
30806
30807     initEvents : function(){
30808         // Roo.form.Checkbox.superclass.initEvents.call(this);
30809         // has no events...
30810        
30811     },
30812
30813
30814     getResizeEl : function(){
30815         return this.wrap;
30816     },
30817
30818     getPositionEl : function(){
30819         return this.wrap;
30820     },
30821
30822     // private
30823     onRender : function(ct, position){
30824         
30825         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30826         var style = this.style;
30827         delete this.style;
30828         
30829         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30830         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30831         this.viewEl = this.wrap.createChild({ tag: 'div' });
30832         if (style) {
30833             this.viewEl.applyStyles(style);
30834         }
30835         if (this.width) {
30836             this.viewEl.setWidth(this.width);
30837         }
30838         if (this.height) {
30839             this.viewEl.setHeight(this.height);
30840         }
30841         //if(this.inputValue !== undefined){
30842         //this.setValue(this.value);
30843         
30844         
30845         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30846         
30847         
30848         this.grid.render();
30849         this.grid.getDataSource().on('remove', this.refreshValue, this);
30850         this.grid.getDataSource().on('update', this.refreshValue, this);
30851         this.grid.on('afteredit', this.refreshValue, this);
30852  
30853     },
30854      
30855     
30856     /**
30857      * Sets the value of the item. 
30858      * @param {String} either an object  or a string..
30859      */
30860     setValue : function(v){
30861         //this.value = v;
30862         v = v || []; // empty set..
30863         // this does not seem smart - it really only affects memoryproxy grids..
30864         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30865             var ds = this.grid.getDataSource();
30866             // assumes a json reader..
30867             var data = {}
30868             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30869             ds.loadData( data);
30870         }
30871         // clear selection so it does not get stale.
30872         if (this.grid.sm) { 
30873             this.grid.sm.clearSelections();
30874         }
30875         
30876         Roo.form.GridField.superclass.setValue.call(this, v);
30877         this.refreshValue();
30878         // should load data in the grid really....
30879     },
30880     
30881     // private
30882     refreshValue: function() {
30883          var val = [];
30884         this.grid.getDataSource().each(function(r) {
30885             val.push(r.data);
30886         });
30887         this.el.dom.value = Roo.encode(val);
30888     }
30889     
30890      
30891     
30892     
30893 });/*
30894  * Based on:
30895  * Ext JS Library 1.1.1
30896  * Copyright(c) 2006-2007, Ext JS, LLC.
30897  *
30898  * Originally Released Under LGPL - original licence link has changed is not relivant.
30899  *
30900  * Fork - LGPL
30901  * <script type="text/javascript">
30902  */
30903 /**
30904  * @class Roo.form.DisplayField
30905  * @extends Roo.form.Field
30906  * A generic Field to display non-editable data.
30907  * @cfg {Boolean} closable (true|false) default false
30908  * @constructor
30909  * Creates a new Display Field item.
30910  * @param {Object} config Configuration options
30911  */
30912 Roo.form.DisplayField = function(config){
30913     Roo.form.DisplayField.superclass.constructor.call(this, config);
30914     
30915     this.addEvents({
30916         /**
30917          * @event close
30918          * Fires after the click the close btn
30919              * @param {Roo.form.DisplayField} this
30920              */
30921         close : true
30922     });
30923 };
30924
30925 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30926     inputType:      'hidden',
30927     allowBlank:     true,
30928     readOnly:         true,
30929     
30930  
30931     /**
30932      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30933      */
30934     focusClass : undefined,
30935     /**
30936      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30937      */
30938     fieldClass: 'x-form-field',
30939     
30940      /**
30941      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30942      */
30943     valueRenderer: undefined,
30944     
30945     width: 100,
30946     /**
30947      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30948      * {tag: "input", type: "checkbox", autocomplete: "off"})
30949      */
30950      
30951  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30952  
30953     closable : false,
30954     
30955     onResize : function(){
30956         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30957         
30958     },
30959
30960     initEvents : function(){
30961         // Roo.form.Checkbox.superclass.initEvents.call(this);
30962         // has no events...
30963         
30964         if(this.closable){
30965             this.closeEl.on('click', this.onClose, this);
30966         }
30967        
30968     },
30969
30970
30971     getResizeEl : function(){
30972         return this.wrap;
30973     },
30974
30975     getPositionEl : function(){
30976         return this.wrap;
30977     },
30978
30979     // private
30980     onRender : function(ct, position){
30981         
30982         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30983         //if(this.inputValue !== undefined){
30984         this.wrap = this.el.wrap();
30985         
30986         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30987         
30988         if(this.closable){
30989             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
30990         }
30991         
30992         if (this.bodyStyle) {
30993             this.viewEl.applyStyles(this.bodyStyle);
30994         }
30995         //this.viewEl.setStyle('padding', '2px');
30996         
30997         this.setValue(this.value);
30998         
30999     },
31000 /*
31001     // private
31002     initValue : Roo.emptyFn,
31003
31004   */
31005
31006         // private
31007     onClick : function(){
31008         
31009     },
31010
31011     /**
31012      * Sets the checked state of the checkbox.
31013      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31014      */
31015     setValue : function(v){
31016         this.value = v;
31017         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31018         // this might be called before we have a dom element..
31019         if (!this.viewEl) {
31020             return;
31021         }
31022         this.viewEl.dom.innerHTML = html;
31023         Roo.form.DisplayField.superclass.setValue.call(this, v);
31024
31025     },
31026     
31027     onClose : function(e)
31028     {
31029         e.preventDefault();
31030         
31031         this.fireEvent('close', this);
31032     }
31033 });/*
31034  * 
31035  * Licence- LGPL
31036  * 
31037  */
31038
31039 /**
31040  * @class Roo.form.DayPicker
31041  * @extends Roo.form.Field
31042  * A Day picker show [M] [T] [W] ....
31043  * @constructor
31044  * Creates a new Day Picker
31045  * @param {Object} config Configuration options
31046  */
31047 Roo.form.DayPicker= function(config){
31048     Roo.form.DayPicker.superclass.constructor.call(this, config);
31049      
31050 };
31051
31052 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31053     /**
31054      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31055      */
31056     focusClass : undefined,
31057     /**
31058      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31059      */
31060     fieldClass: "x-form-field",
31061    
31062     /**
31063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31064      * {tag: "input", type: "checkbox", autocomplete: "off"})
31065      */
31066     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31067     
31068    
31069     actionMode : 'viewEl', 
31070     //
31071     // private
31072  
31073     inputType : 'hidden',
31074     
31075      
31076     inputElement: false, // real input element?
31077     basedOn: false, // ????
31078     
31079     isFormField: true, // not sure where this is needed!!!!
31080
31081     onResize : function(){
31082         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31083         if(!this.boxLabel){
31084             this.el.alignTo(this.wrap, 'c-c');
31085         }
31086     },
31087
31088     initEvents : function(){
31089         Roo.form.Checkbox.superclass.initEvents.call(this);
31090         this.el.on("click", this.onClick,  this);
31091         this.el.on("change", this.onClick,  this);
31092     },
31093
31094
31095     getResizeEl : function(){
31096         return this.wrap;
31097     },
31098
31099     getPositionEl : function(){
31100         return this.wrap;
31101     },
31102
31103     
31104     // private
31105     onRender : function(ct, position){
31106         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31107        
31108         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31109         
31110         var r1 = '<table><tr>';
31111         var r2 = '<tr class="x-form-daypick-icons">';
31112         for (var i=0; i < 7; i++) {
31113             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31114             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31115         }
31116         
31117         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31118         viewEl.select('img').on('click', this.onClick, this);
31119         this.viewEl = viewEl;   
31120         
31121         
31122         // this will not work on Chrome!!!
31123         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31124         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31125         
31126         
31127           
31128
31129     },
31130
31131     // private
31132     initValue : Roo.emptyFn,
31133
31134     /**
31135      * Returns the checked state of the checkbox.
31136      * @return {Boolean} True if checked, else false
31137      */
31138     getValue : function(){
31139         return this.el.dom.value;
31140         
31141     },
31142
31143         // private
31144     onClick : function(e){ 
31145         //this.setChecked(!this.checked);
31146         Roo.get(e.target).toggleClass('x-menu-item-checked');
31147         this.refreshValue();
31148         //if(this.el.dom.checked != this.checked){
31149         //    this.setValue(this.el.dom.checked);
31150        // }
31151     },
31152     
31153     // private
31154     refreshValue : function()
31155     {
31156         var val = '';
31157         this.viewEl.select('img',true).each(function(e,i,n)  {
31158             val += e.is(".x-menu-item-checked") ? String(n) : '';
31159         });
31160         this.setValue(val, true);
31161     },
31162
31163     /**
31164      * Sets the checked state of the checkbox.
31165      * On is always based on a string comparison between inputValue and the param.
31166      * @param {Boolean/String} value - the value to set 
31167      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31168      */
31169     setValue : function(v,suppressEvent){
31170         if (!this.el.dom) {
31171             return;
31172         }
31173         var old = this.el.dom.value ;
31174         this.el.dom.value = v;
31175         if (suppressEvent) {
31176             return ;
31177         }
31178          
31179         // update display..
31180         this.viewEl.select('img',true).each(function(e,i,n)  {
31181             
31182             var on = e.is(".x-menu-item-checked");
31183             var newv = v.indexOf(String(n)) > -1;
31184             if (on != newv) {
31185                 e.toggleClass('x-menu-item-checked');
31186             }
31187             
31188         });
31189         
31190         
31191         this.fireEvent('change', this, v, old);
31192         
31193         
31194     },
31195    
31196     // handle setting of hidden value by some other method!!?!?
31197     setFromHidden: function()
31198     {
31199         if(!this.el){
31200             return;
31201         }
31202         //console.log("SET FROM HIDDEN");
31203         //alert('setFrom hidden');
31204         this.setValue(this.el.dom.value);
31205     },
31206     
31207     onDestroy : function()
31208     {
31209         if(this.viewEl){
31210             Roo.get(this.viewEl).remove();
31211         }
31212          
31213         Roo.form.DayPicker.superclass.onDestroy.call(this);
31214     }
31215
31216 });/*
31217  * RooJS Library 1.1.1
31218  * Copyright(c) 2008-2011  Alan Knowles
31219  *
31220  * License - LGPL
31221  */
31222  
31223
31224 /**
31225  * @class Roo.form.ComboCheck
31226  * @extends Roo.form.ComboBox
31227  * A combobox for multiple select items.
31228  *
31229  * FIXME - could do with a reset button..
31230  * 
31231  * @constructor
31232  * Create a new ComboCheck
31233  * @param {Object} config Configuration options
31234  */
31235 Roo.form.ComboCheck = function(config){
31236     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31237     // should verify some data...
31238     // like
31239     // hiddenName = required..
31240     // displayField = required
31241     // valudField == required
31242     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31243     var _t = this;
31244     Roo.each(req, function(e) {
31245         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31246             throw "Roo.form.ComboCheck : missing value for: " + e;
31247         }
31248     });
31249     
31250     
31251 };
31252
31253 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31254      
31255      
31256     editable : false,
31257      
31258     selectedClass: 'x-menu-item-checked', 
31259     
31260     // private
31261     onRender : function(ct, position){
31262         var _t = this;
31263         
31264         
31265         
31266         if(!this.tpl){
31267             var cls = 'x-combo-list';
31268
31269             
31270             this.tpl =  new Roo.Template({
31271                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31272                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31273                    '<span>{' + this.displayField + '}</span>' +
31274                     '</div>' 
31275                 
31276             });
31277         }
31278  
31279         
31280         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31281         this.view.singleSelect = false;
31282         this.view.multiSelect = true;
31283         this.view.toggleSelect = true;
31284         this.pageTb.add(new Roo.Toolbar.Fill(), {
31285             
31286             text: 'Done',
31287             handler: function()
31288             {
31289                 _t.collapse();
31290             }
31291         });
31292     },
31293     
31294     onViewOver : function(e, t){
31295         // do nothing...
31296         return;
31297         
31298     },
31299     
31300     onViewClick : function(doFocus,index){
31301         return;
31302         
31303     },
31304     select: function () {
31305         //Roo.log("SELECT CALLED");
31306     },
31307      
31308     selectByValue : function(xv, scrollIntoView){
31309         var ar = this.getValueArray();
31310         var sels = [];
31311         
31312         Roo.each(ar, function(v) {
31313             if(v === undefined || v === null){
31314                 return;
31315             }
31316             var r = this.findRecord(this.valueField, v);
31317             if(r){
31318                 sels.push(this.store.indexOf(r))
31319                 
31320             }
31321         },this);
31322         this.view.select(sels);
31323         return false;
31324     },
31325     
31326     
31327     
31328     onSelect : function(record, index){
31329        // Roo.log("onselect Called");
31330        // this is only called by the clear button now..
31331         this.view.clearSelections();
31332         this.setValue('[]');
31333         if (this.value != this.valueBefore) {
31334             this.fireEvent('change', this, this.value, this.valueBefore);
31335             this.valueBefore = this.value;
31336         }
31337     },
31338     getValueArray : function()
31339     {
31340         var ar = [] ;
31341         
31342         try {
31343             //Roo.log(this.value);
31344             if (typeof(this.value) == 'undefined') {
31345                 return [];
31346             }
31347             var ar = Roo.decode(this.value);
31348             return  ar instanceof Array ? ar : []; //?? valid?
31349             
31350         } catch(e) {
31351             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31352             return [];
31353         }
31354          
31355     },
31356     expand : function ()
31357     {
31358         
31359         Roo.form.ComboCheck.superclass.expand.call(this);
31360         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31361         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31362         
31363
31364     },
31365     
31366     collapse : function(){
31367         Roo.form.ComboCheck.superclass.collapse.call(this);
31368         var sl = this.view.getSelectedIndexes();
31369         var st = this.store;
31370         var nv = [];
31371         var tv = [];
31372         var r;
31373         Roo.each(sl, function(i) {
31374             r = st.getAt(i);
31375             nv.push(r.get(this.valueField));
31376         },this);
31377         this.setValue(Roo.encode(nv));
31378         if (this.value != this.valueBefore) {
31379
31380             this.fireEvent('change', this, this.value, this.valueBefore);
31381             this.valueBefore = this.value;
31382         }
31383         
31384     },
31385     
31386     setValue : function(v){
31387         // Roo.log(v);
31388         this.value = v;
31389         
31390         var vals = this.getValueArray();
31391         var tv = [];
31392         Roo.each(vals, function(k) {
31393             var r = this.findRecord(this.valueField, k);
31394             if(r){
31395                 tv.push(r.data[this.displayField]);
31396             }else if(this.valueNotFoundText !== undefined){
31397                 tv.push( this.valueNotFoundText );
31398             }
31399         },this);
31400        // Roo.log(tv);
31401         
31402         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31403         this.hiddenField.value = v;
31404         this.value = v;
31405     }
31406     
31407 });/*
31408  * Based on:
31409  * Ext JS Library 1.1.1
31410  * Copyright(c) 2006-2007, Ext JS, LLC.
31411  *
31412  * Originally Released Under LGPL - original licence link has changed is not relivant.
31413  *
31414  * Fork - LGPL
31415  * <script type="text/javascript">
31416  */
31417  
31418 /**
31419  * @class Roo.form.Signature
31420  * @extends Roo.form.Field
31421  * Signature field.  
31422  * @constructor
31423  * 
31424  * @param {Object} config Configuration options
31425  */
31426
31427 Roo.form.Signature = function(config){
31428     Roo.form.Signature.superclass.constructor.call(this, config);
31429     
31430     this.addEvents({// not in used??
31431          /**
31432          * @event confirm
31433          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31434              * @param {Roo.form.Signature} combo This combo box
31435              */
31436         'confirm' : true,
31437         /**
31438          * @event reset
31439          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31440              * @param {Roo.form.ComboBox} combo This combo box
31441              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31442              */
31443         'reset' : true
31444     });
31445 };
31446
31447 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31448     /**
31449      * @cfg {Object} labels Label to use when rendering a form.
31450      * defaults to 
31451      * labels : { 
31452      *      clear : "Clear",
31453      *      confirm : "Confirm"
31454      *  }
31455      */
31456     labels : { 
31457         clear : "Clear",
31458         confirm : "Confirm"
31459     },
31460     /**
31461      * @cfg {Number} width The signature panel width (defaults to 300)
31462      */
31463     width: 300,
31464     /**
31465      * @cfg {Number} height The signature panel height (defaults to 100)
31466      */
31467     height : 100,
31468     /**
31469      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31470      */
31471     allowBlank : false,
31472     
31473     //private
31474     // {Object} signPanel The signature SVG panel element (defaults to {})
31475     signPanel : {},
31476     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31477     isMouseDown : false,
31478     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31479     isConfirmed : false,
31480     // {String} signatureTmp SVG mapping string (defaults to empty string)
31481     signatureTmp : '',
31482     
31483     
31484     defaultAutoCreate : { // modified by initCompnoent..
31485         tag: "input",
31486         type:"hidden"
31487     },
31488
31489     // private
31490     onRender : function(ct, position){
31491         
31492         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31493         
31494         this.wrap = this.el.wrap({
31495             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31496         });
31497         
31498         this.createToolbar(this);
31499         this.signPanel = this.wrap.createChild({
31500                 tag: 'div',
31501                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31502             }, this.el
31503         );
31504             
31505         this.svgID = Roo.id();
31506         this.svgEl = this.signPanel.createChild({
31507               xmlns : 'http://www.w3.org/2000/svg',
31508               tag : 'svg',
31509               id : this.svgID + "-svg",
31510               width: this.width,
31511               height: this.height,
31512               viewBox: '0 0 '+this.width+' '+this.height,
31513               cn : [
31514                 {
31515                     tag: "rect",
31516                     id: this.svgID + "-svg-r",
31517                     width: this.width,
31518                     height: this.height,
31519                     fill: "#ffa"
31520                 },
31521                 {
31522                     tag: "line",
31523                     id: this.svgID + "-svg-l",
31524                     x1: "0", // start
31525                     y1: (this.height*0.8), // start set the line in 80% of height
31526                     x2: this.width, // end
31527                     y2: (this.height*0.8), // end set the line in 80% of height
31528                     'stroke': "#666",
31529                     'stroke-width': "1",
31530                     'stroke-dasharray': "3",
31531                     'shape-rendering': "crispEdges",
31532                     'pointer-events': "none"
31533                 },
31534                 {
31535                     tag: "path",
31536                     id: this.svgID + "-svg-p",
31537                     'stroke': "navy",
31538                     'stroke-width': "3",
31539                     'fill': "none",
31540                     'pointer-events': 'none'
31541                 }
31542               ]
31543         });
31544         this.createSVG();
31545         this.svgBox = this.svgEl.dom.getScreenCTM();
31546     },
31547     createSVG : function(){ 
31548         var svg = this.signPanel;
31549         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31550         var t = this;
31551
31552         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31553         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31554         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31555         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31556         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31557         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31558         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31559         
31560     },
31561     isTouchEvent : function(e){
31562         return e.type.match(/^touch/);
31563     },
31564     getCoords : function (e) {
31565         var pt    = this.svgEl.dom.createSVGPoint();
31566         pt.x = e.clientX; 
31567         pt.y = e.clientY;
31568         if (this.isTouchEvent(e)) {
31569             pt.x =  e.targetTouches[0].clientX;
31570             pt.y = e.targetTouches[0].clientY;
31571         }
31572         var a = this.svgEl.dom.getScreenCTM();
31573         var b = a.inverse();
31574         var mx = pt.matrixTransform(b);
31575         return mx.x + ',' + mx.y;
31576     },
31577     //mouse event headler 
31578     down : function (e) {
31579         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31580         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31581         
31582         this.isMouseDown = true;
31583         
31584         e.preventDefault();
31585     },
31586     move : function (e) {
31587         if (this.isMouseDown) {
31588             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31589             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31590         }
31591         
31592         e.preventDefault();
31593     },
31594     up : function (e) {
31595         this.isMouseDown = false;
31596         var sp = this.signatureTmp.split(' ');
31597         
31598         if(sp.length > 1){
31599             if(!sp[sp.length-2].match(/^L/)){
31600                 sp.pop();
31601                 sp.pop();
31602                 sp.push("");
31603                 this.signatureTmp = sp.join(" ");
31604             }
31605         }
31606         if(this.getValue() != this.signatureTmp){
31607             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31608             this.isConfirmed = false;
31609         }
31610         e.preventDefault();
31611     },
31612     
31613     /**
31614      * Protected method that will not generally be called directly. It
31615      * is called when the editor creates its toolbar. Override this method if you need to
31616      * add custom toolbar buttons.
31617      * @param {HtmlEditor} editor
31618      */
31619     createToolbar : function(editor){
31620          function btn(id, toggle, handler){
31621             var xid = fid + '-'+ id ;
31622             return {
31623                 id : xid,
31624                 cmd : id,
31625                 cls : 'x-btn-icon x-edit-'+id,
31626                 enableToggle:toggle !== false,
31627                 scope: editor, // was editor...
31628                 handler:handler||editor.relayBtnCmd,
31629                 clickEvent:'mousedown',
31630                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31631                 tabIndex:-1
31632             };
31633         }
31634         
31635         
31636         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31637         this.tb = tb;
31638         this.tb.add(
31639            {
31640                 cls : ' x-signature-btn x-signature-'+id,
31641                 scope: editor, // was editor...
31642                 handler: this.reset,
31643                 clickEvent:'mousedown',
31644                 text: this.labels.clear
31645             },
31646             {
31647                  xtype : 'Fill',
31648                  xns: Roo.Toolbar
31649             }, 
31650             {
31651                 cls : '  x-signature-btn x-signature-'+id,
31652                 scope: editor, // was editor...
31653                 handler: this.confirmHandler,
31654                 clickEvent:'mousedown',
31655                 text: this.labels.confirm
31656             }
31657         );
31658     
31659     },
31660     //public
31661     /**
31662      * when user is clicked confirm then show this image.....
31663      * 
31664      * @return {String} Image Data URI
31665      */
31666     getImageDataURI : function(){
31667         var svg = this.svgEl.dom.parentNode.innerHTML;
31668         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31669         return src; 
31670     },
31671     /**
31672      * 
31673      * @return {Boolean} this.isConfirmed
31674      */
31675     getConfirmed : function(){
31676         return this.isConfirmed;
31677     },
31678     /**
31679      * 
31680      * @return {Number} this.width
31681      */
31682     getWidth : function(){
31683         return this.width;
31684     },
31685     /**
31686      * 
31687      * @return {Number} this.height
31688      */
31689     getHeight : function(){
31690         return this.height;
31691     },
31692     // private
31693     getSignature : function(){
31694         return this.signatureTmp;
31695     },
31696     // private
31697     reset : function(){
31698         this.signatureTmp = '';
31699         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31700         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31701         this.isConfirmed = false;
31702         Roo.form.Signature.superclass.reset.call(this);
31703     },
31704     setSignature : function(s){
31705         this.signatureTmp = s;
31706         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31707         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31708         this.setValue(s);
31709         this.isConfirmed = false;
31710         Roo.form.Signature.superclass.reset.call(this);
31711     }, 
31712     test : function(){
31713 //        Roo.log(this.signPanel.dom.contentWindow.up())
31714     },
31715     //private
31716     setConfirmed : function(){
31717         
31718         
31719         
31720 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31721     },
31722     // private
31723     confirmHandler : function(){
31724         if(!this.getSignature()){
31725             return;
31726         }
31727         
31728         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31729         this.setValue(this.getSignature());
31730         this.isConfirmed = true;
31731         
31732         this.fireEvent('confirm', this);
31733     },
31734     // private
31735     // Subclasses should provide the validation implementation by overriding this
31736     validateValue : function(value){
31737         if(this.allowBlank){
31738             return true;
31739         }
31740         
31741         if(this.isConfirmed){
31742             return true;
31743         }
31744         return false;
31745     }
31746 });/*
31747  * Based on:
31748  * Ext JS Library 1.1.1
31749  * Copyright(c) 2006-2007, Ext JS, LLC.
31750  *
31751  * Originally Released Under LGPL - original licence link has changed is not relivant.
31752  *
31753  * Fork - LGPL
31754  * <script type="text/javascript">
31755  */
31756  
31757
31758 /**
31759  * @class Roo.form.ComboBox
31760  * @extends Roo.form.TriggerField
31761  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31762  * @constructor
31763  * Create a new ComboBox.
31764  * @param {Object} config Configuration options
31765  */
31766 Roo.form.Select = function(config){
31767     Roo.form.Select.superclass.constructor.call(this, config);
31768      
31769 };
31770
31771 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31772     /**
31773      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31774      */
31775     /**
31776      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31777      * rendering into an Roo.Editor, defaults to false)
31778      */
31779     /**
31780      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31781      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31782      */
31783     /**
31784      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31785      */
31786     /**
31787      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31788      * the dropdown list (defaults to undefined, with no header element)
31789      */
31790
31791      /**
31792      * @cfg {String/Roo.Template} tpl The template to use to render the output
31793      */
31794      
31795     // private
31796     defaultAutoCreate : {tag: "select"  },
31797     /**
31798      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31799      */
31800     listWidth: undefined,
31801     /**
31802      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31803      * mode = 'remote' or 'text' if mode = 'local')
31804      */
31805     displayField: undefined,
31806     /**
31807      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31808      * mode = 'remote' or 'value' if mode = 'local'). 
31809      * Note: use of a valueField requires the user make a selection
31810      * in order for a value to be mapped.
31811      */
31812     valueField: undefined,
31813     
31814     
31815     /**
31816      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31817      * field's data value (defaults to the underlying DOM element's name)
31818      */
31819     hiddenName: undefined,
31820     /**
31821      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31822      */
31823     listClass: '',
31824     /**
31825      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31826      */
31827     selectedClass: 'x-combo-selected',
31828     /**
31829      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31830      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31831      * which displays a downward arrow icon).
31832      */
31833     triggerClass : 'x-form-arrow-trigger',
31834     /**
31835      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31836      */
31837     shadow:'sides',
31838     /**
31839      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31840      * anchor positions (defaults to 'tl-bl')
31841      */
31842     listAlign: 'tl-bl?',
31843     /**
31844      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31845      */
31846     maxHeight: 300,
31847     /**
31848      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31849      * query specified by the allQuery config option (defaults to 'query')
31850      */
31851     triggerAction: 'query',
31852     /**
31853      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31854      * (defaults to 4, does not apply if editable = false)
31855      */
31856     minChars : 4,
31857     /**
31858      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31859      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31860      */
31861     typeAhead: false,
31862     /**
31863      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31864      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31865      */
31866     queryDelay: 500,
31867     /**
31868      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31869      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31870      */
31871     pageSize: 0,
31872     /**
31873      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31874      * when editable = true (defaults to false)
31875      */
31876     selectOnFocus:false,
31877     /**
31878      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31879      */
31880     queryParam: 'query',
31881     /**
31882      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31883      * when mode = 'remote' (defaults to 'Loading...')
31884      */
31885     loadingText: 'Loading...',
31886     /**
31887      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31888      */
31889     resizable: false,
31890     /**
31891      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31892      */
31893     handleHeight : 8,
31894     /**
31895      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31896      * traditional select (defaults to true)
31897      */
31898     editable: true,
31899     /**
31900      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31901      */
31902     allQuery: '',
31903     /**
31904      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31905      */
31906     mode: 'remote',
31907     /**
31908      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31909      * listWidth has a higher value)
31910      */
31911     minListWidth : 70,
31912     /**
31913      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31914      * allow the user to set arbitrary text into the field (defaults to false)
31915      */
31916     forceSelection:false,
31917     /**
31918      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31919      * if typeAhead = true (defaults to 250)
31920      */
31921     typeAheadDelay : 250,
31922     /**
31923      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31924      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31925      */
31926     valueNotFoundText : undefined,
31927     
31928     /**
31929      * @cfg {String} defaultValue The value displayed after loading the store.
31930      */
31931     defaultValue: '',
31932     
31933     /**
31934      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31935      */
31936     blockFocus : false,
31937     
31938     /**
31939      * @cfg {Boolean} disableClear Disable showing of clear button.
31940      */
31941     disableClear : false,
31942     /**
31943      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31944      */
31945     alwaysQuery : false,
31946     
31947     //private
31948     addicon : false,
31949     editicon: false,
31950     
31951     // element that contains real text value.. (when hidden is used..)
31952      
31953     // private
31954     onRender : function(ct, position){
31955         Roo.form.Field.prototype.onRender.call(this, ct, position);
31956         
31957         if(this.store){
31958             this.store.on('beforeload', this.onBeforeLoad, this);
31959             this.store.on('load', this.onLoad, this);
31960             this.store.on('loadexception', this.onLoadException, this);
31961             this.store.load({});
31962         }
31963         
31964         
31965         
31966     },
31967
31968     // private
31969     initEvents : function(){
31970         //Roo.form.ComboBox.superclass.initEvents.call(this);
31971  
31972     },
31973
31974     onDestroy : function(){
31975        
31976         if(this.store){
31977             this.store.un('beforeload', this.onBeforeLoad, this);
31978             this.store.un('load', this.onLoad, this);
31979             this.store.un('loadexception', this.onLoadException, this);
31980         }
31981         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31982     },
31983
31984     // private
31985     fireKey : function(e){
31986         if(e.isNavKeyPress() && !this.list.isVisible()){
31987             this.fireEvent("specialkey", this, e);
31988         }
31989     },
31990
31991     // private
31992     onResize: function(w, h){
31993         
31994         return; 
31995     
31996         
31997     },
31998
31999     /**
32000      * Allow or prevent the user from directly editing the field text.  If false is passed,
32001      * the user will only be able to select from the items defined in the dropdown list.  This method
32002      * is the runtime equivalent of setting the 'editable' config option at config time.
32003      * @param {Boolean} value True to allow the user to directly edit the field text
32004      */
32005     setEditable : function(value){
32006          
32007     },
32008
32009     // private
32010     onBeforeLoad : function(){
32011         
32012         Roo.log("Select before load");
32013         return;
32014     
32015         this.innerList.update(this.loadingText ?
32016                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32017         //this.restrictHeight();
32018         this.selectedIndex = -1;
32019     },
32020
32021     // private
32022     onLoad : function(){
32023
32024     
32025         var dom = this.el.dom;
32026         dom.innerHTML = '';
32027          var od = dom.ownerDocument;
32028          
32029         if (this.emptyText) {
32030             var op = od.createElement('option');
32031             op.setAttribute('value', '');
32032             op.innerHTML = String.format('{0}', this.emptyText);
32033             dom.appendChild(op);
32034         }
32035         if(this.store.getCount() > 0){
32036            
32037             var vf = this.valueField;
32038             var df = this.displayField;
32039             this.store.data.each(function(r) {
32040                 // which colmsn to use... testing - cdoe / title..
32041                 var op = od.createElement('option');
32042                 op.setAttribute('value', r.data[vf]);
32043                 op.innerHTML = String.format('{0}', r.data[df]);
32044                 dom.appendChild(op);
32045             });
32046             if (typeof(this.defaultValue != 'undefined')) {
32047                 this.setValue(this.defaultValue);
32048             }
32049             
32050              
32051         }else{
32052             //this.onEmptyResults();
32053         }
32054         //this.el.focus();
32055     },
32056     // private
32057     onLoadException : function()
32058     {
32059         dom.innerHTML = '';
32060             
32061         Roo.log("Select on load exception");
32062         return;
32063     
32064         this.collapse();
32065         Roo.log(this.store.reader.jsonData);
32066         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32067             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32068         }
32069         
32070         
32071     },
32072     // private
32073     onTypeAhead : function(){
32074          
32075     },
32076
32077     // private
32078     onSelect : function(record, index){
32079         Roo.log('on select?');
32080         return;
32081         if(this.fireEvent('beforeselect', this, record, index) !== false){
32082             this.setFromData(index > -1 ? record.data : false);
32083             this.collapse();
32084             this.fireEvent('select', this, record, index);
32085         }
32086     },
32087
32088     /**
32089      * Returns the currently selected field value or empty string if no value is set.
32090      * @return {String} value The selected value
32091      */
32092     getValue : function(){
32093         var dom = this.el.dom;
32094         this.value = dom.options[dom.selectedIndex].value;
32095         return this.value;
32096         
32097     },
32098
32099     /**
32100      * Clears any text/value currently set in the field
32101      */
32102     clearValue : function(){
32103         this.value = '';
32104         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32105         
32106     },
32107
32108     /**
32109      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32110      * will be displayed in the field.  If the value does not match the data value of an existing item,
32111      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32112      * Otherwise the field will be blank (although the value will still be set).
32113      * @param {String} value The value to match
32114      */
32115     setValue : function(v){
32116         var d = this.el.dom;
32117         for (var i =0; i < d.options.length;i++) {
32118             if (v == d.options[i].value) {
32119                 d.selectedIndex = i;
32120                 this.value = v;
32121                 return;
32122             }
32123         }
32124         this.clearValue();
32125     },
32126     /**
32127      * @property {Object} the last set data for the element
32128      */
32129     
32130     lastData : false,
32131     /**
32132      * Sets the value of the field based on a object which is related to the record format for the store.
32133      * @param {Object} value the value to set as. or false on reset?
32134      */
32135     setFromData : function(o){
32136         Roo.log('setfrom data?');
32137          
32138         
32139         
32140     },
32141     // private
32142     reset : function(){
32143         this.clearValue();
32144     },
32145     // private
32146     findRecord : function(prop, value){
32147         
32148         return false;
32149     
32150         var record;
32151         if(this.store.getCount() > 0){
32152             this.store.each(function(r){
32153                 if(r.data[prop] == value){
32154                     record = r;
32155                     return false;
32156                 }
32157                 return true;
32158             });
32159         }
32160         return record;
32161     },
32162     
32163     getName: function()
32164     {
32165         // returns hidden if it's set..
32166         if (!this.rendered) {return ''};
32167         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32168         
32169     },
32170      
32171
32172     
32173
32174     // private
32175     onEmptyResults : function(){
32176         Roo.log('empty results');
32177         //this.collapse();
32178     },
32179
32180     /**
32181      * Returns true if the dropdown list is expanded, else false.
32182      */
32183     isExpanded : function(){
32184         return false;
32185     },
32186
32187     /**
32188      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32189      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32190      * @param {String} value The data value of the item to select
32191      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32192      * selected item if it is not currently in view (defaults to true)
32193      * @return {Boolean} True if the value matched an item in the list, else false
32194      */
32195     selectByValue : function(v, scrollIntoView){
32196         Roo.log('select By Value');
32197         return false;
32198     
32199         if(v !== undefined && v !== null){
32200             var r = this.findRecord(this.valueField || this.displayField, v);
32201             if(r){
32202                 this.select(this.store.indexOf(r), scrollIntoView);
32203                 return true;
32204             }
32205         }
32206         return false;
32207     },
32208
32209     /**
32210      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32211      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32212      * @param {Number} index The zero-based index of the list item to select
32213      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32214      * selected item if it is not currently in view (defaults to true)
32215      */
32216     select : function(index, scrollIntoView){
32217         Roo.log('select ');
32218         return  ;
32219         
32220         this.selectedIndex = index;
32221         this.view.select(index);
32222         if(scrollIntoView !== false){
32223             var el = this.view.getNode(index);
32224             if(el){
32225                 this.innerList.scrollChildIntoView(el, false);
32226             }
32227         }
32228     },
32229
32230       
32231
32232     // private
32233     validateBlur : function(){
32234         
32235         return;
32236         
32237     },
32238
32239     // private
32240     initQuery : function(){
32241         this.doQuery(this.getRawValue());
32242     },
32243
32244     // private
32245     doForce : function(){
32246         if(this.el.dom.value.length > 0){
32247             this.el.dom.value =
32248                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32249              
32250         }
32251     },
32252
32253     /**
32254      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32255      * query allowing the query action to be canceled if needed.
32256      * @param {String} query The SQL query to execute
32257      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32258      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32259      * saved in the current store (defaults to false)
32260      */
32261     doQuery : function(q, forceAll){
32262         
32263         Roo.log('doQuery?');
32264         if(q === undefined || q === null){
32265             q = '';
32266         }
32267         var qe = {
32268             query: q,
32269             forceAll: forceAll,
32270             combo: this,
32271             cancel:false
32272         };
32273         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32274             return false;
32275         }
32276         q = qe.query;
32277         forceAll = qe.forceAll;
32278         if(forceAll === true || (q.length >= this.minChars)){
32279             if(this.lastQuery != q || this.alwaysQuery){
32280                 this.lastQuery = q;
32281                 if(this.mode == 'local'){
32282                     this.selectedIndex = -1;
32283                     if(forceAll){
32284                         this.store.clearFilter();
32285                     }else{
32286                         this.store.filter(this.displayField, q);
32287                     }
32288                     this.onLoad();
32289                 }else{
32290                     this.store.baseParams[this.queryParam] = q;
32291                     this.store.load({
32292                         params: this.getParams(q)
32293                     });
32294                     this.expand();
32295                 }
32296             }else{
32297                 this.selectedIndex = -1;
32298                 this.onLoad();   
32299             }
32300         }
32301     },
32302
32303     // private
32304     getParams : function(q){
32305         var p = {};
32306         //p[this.queryParam] = q;
32307         if(this.pageSize){
32308             p.start = 0;
32309             p.limit = this.pageSize;
32310         }
32311         return p;
32312     },
32313
32314     /**
32315      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32316      */
32317     collapse : function(){
32318         
32319     },
32320
32321     // private
32322     collapseIf : function(e){
32323         
32324     },
32325
32326     /**
32327      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32328      */
32329     expand : function(){
32330         
32331     } ,
32332
32333     // private
32334      
32335
32336     /** 
32337     * @cfg {Boolean} grow 
32338     * @hide 
32339     */
32340     /** 
32341     * @cfg {Number} growMin 
32342     * @hide 
32343     */
32344     /** 
32345     * @cfg {Number} growMax 
32346     * @hide 
32347     */
32348     /**
32349      * @hide
32350      * @method autoSize
32351      */
32352     
32353     setWidth : function()
32354     {
32355         
32356     },
32357     getResizeEl : function(){
32358         return this.el;
32359     }
32360 });//<script type="text/javasscript">
32361  
32362
32363 /**
32364  * @class Roo.DDView
32365  * A DnD enabled version of Roo.View.
32366  * @param {Element/String} container The Element in which to create the View.
32367  * @param {String} tpl The template string used to create the markup for each element of the View
32368  * @param {Object} config The configuration properties. These include all the config options of
32369  * {@link Roo.View} plus some specific to this class.<br>
32370  * <p>
32371  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32372  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32373  * <p>
32374  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32375 .x-view-drag-insert-above {
32376         border-top:1px dotted #3366cc;
32377 }
32378 .x-view-drag-insert-below {
32379         border-bottom:1px dotted #3366cc;
32380 }
32381 </code></pre>
32382  * 
32383  */
32384  
32385 Roo.DDView = function(container, tpl, config) {
32386     Roo.DDView.superclass.constructor.apply(this, arguments);
32387     this.getEl().setStyle("outline", "0px none");
32388     this.getEl().unselectable();
32389     if (this.dragGroup) {
32390                 this.setDraggable(this.dragGroup.split(","));
32391     }
32392     if (this.dropGroup) {
32393                 this.setDroppable(this.dropGroup.split(","));
32394     }
32395     if (this.deletable) {
32396         this.setDeletable();
32397     }
32398     this.isDirtyFlag = false;
32399         this.addEvents({
32400                 "drop" : true
32401         });
32402 };
32403
32404 Roo.extend(Roo.DDView, Roo.View, {
32405 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32406 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32407 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32408 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32409
32410         isFormField: true,
32411
32412         reset: Roo.emptyFn,
32413         
32414         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32415
32416         validate: function() {
32417                 return true;
32418         },
32419         
32420         destroy: function() {
32421                 this.purgeListeners();
32422                 this.getEl.removeAllListeners();
32423                 this.getEl().remove();
32424                 if (this.dragZone) {
32425                         if (this.dragZone.destroy) {
32426                                 this.dragZone.destroy();
32427                         }
32428                 }
32429                 if (this.dropZone) {
32430                         if (this.dropZone.destroy) {
32431                                 this.dropZone.destroy();
32432                         }
32433                 }
32434         },
32435
32436 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32437         getName: function() {
32438                 return this.name;
32439         },
32440
32441 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32442         setValue: function(v) {
32443                 if (!this.store) {
32444                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32445                 }
32446                 var data = {};
32447                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32448                 this.store.proxy = new Roo.data.MemoryProxy(data);
32449                 this.store.load();
32450         },
32451
32452 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32453         getValue: function() {
32454                 var result = '(';
32455                 this.store.each(function(rec) {
32456                         result += rec.id + ',';
32457                 });
32458                 return result.substr(0, result.length - 1) + ')';
32459         },
32460         
32461         getIds: function() {
32462                 var i = 0, result = new Array(this.store.getCount());
32463                 this.store.each(function(rec) {
32464                         result[i++] = rec.id;
32465                 });
32466                 return result;
32467         },
32468         
32469         isDirty: function() {
32470                 return this.isDirtyFlag;
32471         },
32472
32473 /**
32474  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32475  *      whole Element becomes the target, and this causes the drop gesture to append.
32476  */
32477     getTargetFromEvent : function(e) {
32478                 var target = e.getTarget();
32479                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32480                 target = target.parentNode;
32481                 }
32482                 if (!target) {
32483                         target = this.el.dom.lastChild || this.el.dom;
32484                 }
32485                 return target;
32486     },
32487
32488 /**
32489  *      Create the drag data which consists of an object which has the property "ddel" as
32490  *      the drag proxy element. 
32491  */
32492     getDragData : function(e) {
32493         var target = this.findItemFromChild(e.getTarget());
32494                 if(target) {
32495                         this.handleSelection(e);
32496                         var selNodes = this.getSelectedNodes();
32497             var dragData = {
32498                 source: this,
32499                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32500                 nodes: selNodes,
32501                 records: []
32502                         };
32503                         var selectedIndices = this.getSelectedIndexes();
32504                         for (var i = 0; i < selectedIndices.length; i++) {
32505                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32506                         }
32507                         if (selNodes.length == 1) {
32508                                 dragData.ddel = target.cloneNode(true); // the div element
32509                         } else {
32510                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32511                                 div.className = 'multi-proxy';
32512                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32513                                         div.appendChild(selNodes[i].cloneNode(true));
32514                                 }
32515                                 dragData.ddel = div;
32516                         }
32517             //console.log(dragData)
32518             //console.log(dragData.ddel.innerHTML)
32519                         return dragData;
32520                 }
32521         //console.log('nodragData')
32522                 return false;
32523     },
32524     
32525 /**     Specify to which ddGroup items in this DDView may be dragged. */
32526     setDraggable: function(ddGroup) {
32527         if (ddGroup instanceof Array) {
32528                 Roo.each(ddGroup, this.setDraggable, this);
32529                 return;
32530         }
32531         if (this.dragZone) {
32532                 this.dragZone.addToGroup(ddGroup);
32533         } else {
32534                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32535                                 containerScroll: true,
32536                                 ddGroup: ddGroup 
32537
32538                         });
32539 //                      Draggability implies selection. DragZone's mousedown selects the element.
32540                         if (!this.multiSelect) { this.singleSelect = true; }
32541
32542 //                      Wire the DragZone's handlers up to methods in *this*
32543                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32544                 }
32545     },
32546
32547 /**     Specify from which ddGroup this DDView accepts drops. */
32548     setDroppable: function(ddGroup) {
32549         if (ddGroup instanceof Array) {
32550                 Roo.each(ddGroup, this.setDroppable, this);
32551                 return;
32552         }
32553         if (this.dropZone) {
32554                 this.dropZone.addToGroup(ddGroup);
32555         } else {
32556                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32557                                 containerScroll: true,
32558                                 ddGroup: ddGroup
32559                         });
32560
32561 //                      Wire the DropZone's handlers up to methods in *this*
32562                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32563                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32564                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32565                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32566                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32567                 }
32568     },
32569
32570 /**     Decide whether to drop above or below a View node. */
32571     getDropPoint : function(e, n, dd){
32572         if (n == this.el.dom) { return "above"; }
32573                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32574                 var c = t + (b - t) / 2;
32575                 var y = Roo.lib.Event.getPageY(e);
32576                 if(y <= c) {
32577                         return "above";
32578                 }else{
32579                         return "below";
32580                 }
32581     },
32582
32583     onNodeEnter : function(n, dd, e, data){
32584                 return false;
32585     },
32586     
32587     onNodeOver : function(n, dd, e, data){
32588                 var pt = this.getDropPoint(e, n, dd);
32589                 // set the insert point style on the target node
32590                 var dragElClass = this.dropNotAllowed;
32591                 if (pt) {
32592                         var targetElClass;
32593                         if (pt == "above"){
32594                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32595                                 targetElClass = "x-view-drag-insert-above";
32596                         } else {
32597                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32598                                 targetElClass = "x-view-drag-insert-below";
32599                         }
32600                         if (this.lastInsertClass != targetElClass){
32601                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32602                                 this.lastInsertClass = targetElClass;
32603                         }
32604                 }
32605                 return dragElClass;
32606         },
32607
32608     onNodeOut : function(n, dd, e, data){
32609                 this.removeDropIndicators(n);
32610     },
32611
32612     onNodeDrop : function(n, dd, e, data){
32613         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32614                 return false;
32615         }
32616         var pt = this.getDropPoint(e, n, dd);
32617                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32618                 if (pt == "below") { insertAt++; }
32619                 for (var i = 0; i < data.records.length; i++) {
32620                         var r = data.records[i];
32621                         var dup = this.store.getById(r.id);
32622                         if (dup && (dd != this.dragZone)) {
32623                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32624                         } else {
32625                                 if (data.copy) {
32626                                         this.store.insert(insertAt++, r.copy());
32627                                 } else {
32628                                         data.source.isDirtyFlag = true;
32629                                         r.store.remove(r);
32630                                         this.store.insert(insertAt++, r);
32631                                 }
32632                                 this.isDirtyFlag = true;
32633                         }
32634                 }
32635                 this.dragZone.cachedTarget = null;
32636                 return true;
32637     },
32638
32639     removeDropIndicators : function(n){
32640                 if(n){
32641                         Roo.fly(n).removeClass([
32642                                 "x-view-drag-insert-above",
32643                                 "x-view-drag-insert-below"]);
32644                         this.lastInsertClass = "_noclass";
32645                 }
32646     },
32647
32648 /**
32649  *      Utility method. Add a delete option to the DDView's context menu.
32650  *      @param {String} imageUrl The URL of the "delete" icon image.
32651  */
32652         setDeletable: function(imageUrl) {
32653                 if (!this.singleSelect && !this.multiSelect) {
32654                         this.singleSelect = true;
32655                 }
32656                 var c = this.getContextMenu();
32657                 this.contextMenu.on("itemclick", function(item) {
32658                         switch (item.id) {
32659                                 case "delete":
32660                                         this.remove(this.getSelectedIndexes());
32661                                         break;
32662                         }
32663                 }, this);
32664                 this.contextMenu.add({
32665                         icon: imageUrl,
32666                         id: "delete",
32667                         text: 'Delete'
32668                 });
32669         },
32670         
32671 /**     Return the context menu for this DDView. */
32672         getContextMenu: function() {
32673                 if (!this.contextMenu) {
32674 //                      Create the View's context menu
32675                         this.contextMenu = new Roo.menu.Menu({
32676                                 id: this.id + "-contextmenu"
32677                         });
32678                         this.el.on("contextmenu", this.showContextMenu, this);
32679                 }
32680                 return this.contextMenu;
32681         },
32682         
32683         disableContextMenu: function() {
32684                 if (this.contextMenu) {
32685                         this.el.un("contextmenu", this.showContextMenu, this);
32686                 }
32687         },
32688
32689         showContextMenu: function(e, item) {
32690         item = this.findItemFromChild(e.getTarget());
32691                 if (item) {
32692                         e.stopEvent();
32693                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32694                         this.contextMenu.showAt(e.getXY());
32695             }
32696     },
32697
32698 /**
32699  *      Remove {@link Roo.data.Record}s at the specified indices.
32700  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32701  */
32702     remove: function(selectedIndices) {
32703                 selectedIndices = [].concat(selectedIndices);
32704                 for (var i = 0; i < selectedIndices.length; i++) {
32705                         var rec = this.store.getAt(selectedIndices[i]);
32706                         this.store.remove(rec);
32707                 }
32708     },
32709
32710 /**
32711  *      Double click fires the event, but also, if this is draggable, and there is only one other
32712  *      related DropZone, it transfers the selected node.
32713  */
32714     onDblClick : function(e){
32715         var item = this.findItemFromChild(e.getTarget());
32716         if(item){
32717             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32718                 return false;
32719             }
32720             if (this.dragGroup) {
32721                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32722                     while (targets.indexOf(this.dropZone) > -1) {
32723                             targets.remove(this.dropZone);
32724                                 }
32725                     if (targets.length == 1) {
32726                                         this.dragZone.cachedTarget = null;
32727                         var el = Roo.get(targets[0].getEl());
32728                         var box = el.getBox(true);
32729                         targets[0].onNodeDrop(el.dom, {
32730                                 target: el.dom,
32731                                 xy: [box.x, box.y + box.height - 1]
32732                         }, null, this.getDragData(e));
32733                     }
32734                 }
32735         }
32736     },
32737     
32738     handleSelection: function(e) {
32739                 this.dragZone.cachedTarget = null;
32740         var item = this.findItemFromChild(e.getTarget());
32741         if (!item) {
32742                 this.clearSelections(true);
32743                 return;
32744         }
32745                 if (item && (this.multiSelect || this.singleSelect)){
32746                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32747                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32748                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32749                                 this.unselect(item);
32750                         } else {
32751                                 this.select(item, this.multiSelect && e.ctrlKey);
32752                                 this.lastSelection = item;
32753                         }
32754                 }
32755     },
32756
32757     onItemClick : function(item, index, e){
32758                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32759                         return false;
32760                 }
32761                 return true;
32762     },
32763
32764     unselect : function(nodeInfo, suppressEvent){
32765                 var node = this.getNode(nodeInfo);
32766                 if(node && this.isSelected(node)){
32767                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32768                                 Roo.fly(node).removeClass(this.selectedClass);
32769                                 this.selections.remove(node);
32770                                 if(!suppressEvent){
32771                                         this.fireEvent("selectionchange", this, this.selections);
32772                                 }
32773                         }
32774                 }
32775     }
32776 });
32777 /*
32778  * Based on:
32779  * Ext JS Library 1.1.1
32780  * Copyright(c) 2006-2007, Ext JS, LLC.
32781  *
32782  * Originally Released Under LGPL - original licence link has changed is not relivant.
32783  *
32784  * Fork - LGPL
32785  * <script type="text/javascript">
32786  */
32787  
32788 /**
32789  * @class Roo.LayoutManager
32790  * @extends Roo.util.Observable
32791  * Base class for layout managers.
32792  */
32793 Roo.LayoutManager = function(container, config){
32794     Roo.LayoutManager.superclass.constructor.call(this);
32795     this.el = Roo.get(container);
32796     // ie scrollbar fix
32797     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32798         document.body.scroll = "no";
32799     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32800         this.el.position('relative');
32801     }
32802     this.id = this.el.id;
32803     this.el.addClass("x-layout-container");
32804     /** false to disable window resize monitoring @type Boolean */
32805     this.monitorWindowResize = true;
32806     this.regions = {};
32807     this.addEvents({
32808         /**
32809          * @event layout
32810          * Fires when a layout is performed. 
32811          * @param {Roo.LayoutManager} this
32812          */
32813         "layout" : true,
32814         /**
32815          * @event regionresized
32816          * Fires when the user resizes a region. 
32817          * @param {Roo.LayoutRegion} region The resized region
32818          * @param {Number} newSize The new size (width for east/west, height for north/south)
32819          */
32820         "regionresized" : true,
32821         /**
32822          * @event regioncollapsed
32823          * Fires when a region is collapsed. 
32824          * @param {Roo.LayoutRegion} region The collapsed region
32825          */
32826         "regioncollapsed" : true,
32827         /**
32828          * @event regionexpanded
32829          * Fires when a region is expanded.  
32830          * @param {Roo.LayoutRegion} region The expanded region
32831          */
32832         "regionexpanded" : true
32833     });
32834     this.updating = false;
32835     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32836 };
32837
32838 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32839     /**
32840      * Returns true if this layout is currently being updated
32841      * @return {Boolean}
32842      */
32843     isUpdating : function(){
32844         return this.updating; 
32845     },
32846     
32847     /**
32848      * Suspend the LayoutManager from doing auto-layouts while
32849      * making multiple add or remove calls
32850      */
32851     beginUpdate : function(){
32852         this.updating = true;    
32853     },
32854     
32855     /**
32856      * Restore auto-layouts and optionally disable the manager from performing a layout
32857      * @param {Boolean} noLayout true to disable a layout update 
32858      */
32859     endUpdate : function(noLayout){
32860         this.updating = false;
32861         if(!noLayout){
32862             this.layout();
32863         }    
32864     },
32865     
32866     layout: function(){
32867         
32868     },
32869     
32870     onRegionResized : function(region, newSize){
32871         this.fireEvent("regionresized", region, newSize);
32872         this.layout();
32873     },
32874     
32875     onRegionCollapsed : function(region){
32876         this.fireEvent("regioncollapsed", region);
32877     },
32878     
32879     onRegionExpanded : function(region){
32880         this.fireEvent("regionexpanded", region);
32881     },
32882         
32883     /**
32884      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32885      * performs box-model adjustments.
32886      * @return {Object} The size as an object {width: (the width), height: (the height)}
32887      */
32888     getViewSize : function(){
32889         var size;
32890         if(this.el.dom != document.body){
32891             size = this.el.getSize();
32892         }else{
32893             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32894         }
32895         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32896         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32897         return size;
32898     },
32899     
32900     /**
32901      * Returns the Element this layout is bound to.
32902      * @return {Roo.Element}
32903      */
32904     getEl : function(){
32905         return this.el;
32906     },
32907     
32908     /**
32909      * Returns the specified region.
32910      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32911      * @return {Roo.LayoutRegion}
32912      */
32913     getRegion : function(target){
32914         return this.regions[target.toLowerCase()];
32915     },
32916     
32917     onWindowResize : function(){
32918         if(this.monitorWindowResize){
32919             this.layout();
32920         }
32921     }
32922 });/*
32923  * Based on:
32924  * Ext JS Library 1.1.1
32925  * Copyright(c) 2006-2007, Ext JS, LLC.
32926  *
32927  * Originally Released Under LGPL - original licence link has changed is not relivant.
32928  *
32929  * Fork - LGPL
32930  * <script type="text/javascript">
32931  */
32932 /**
32933  * @class Roo.BorderLayout
32934  * @extends Roo.LayoutManager
32935  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32936  * please see: <br><br>
32937  * <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>
32938  * <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>
32939  * Example:
32940  <pre><code>
32941  var layout = new Roo.BorderLayout(document.body, {
32942     north: {
32943         initialSize: 25,
32944         titlebar: false
32945     },
32946     west: {
32947         split:true,
32948         initialSize: 200,
32949         minSize: 175,
32950         maxSize: 400,
32951         titlebar: true,
32952         collapsible: true
32953     },
32954     east: {
32955         split:true,
32956         initialSize: 202,
32957         minSize: 175,
32958         maxSize: 400,
32959         titlebar: true,
32960         collapsible: true
32961     },
32962     south: {
32963         split:true,
32964         initialSize: 100,
32965         minSize: 100,
32966         maxSize: 200,
32967         titlebar: true,
32968         collapsible: true
32969     },
32970     center: {
32971         titlebar: true,
32972         autoScroll:true,
32973         resizeTabs: true,
32974         minTabWidth: 50,
32975         preferredTabWidth: 150
32976     }
32977 });
32978
32979 // shorthand
32980 var CP = Roo.ContentPanel;
32981
32982 layout.beginUpdate();
32983 layout.add("north", new CP("north", "North"));
32984 layout.add("south", new CP("south", {title: "South", closable: true}));
32985 layout.add("west", new CP("west", {title: "West"}));
32986 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32987 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32988 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32989 layout.getRegion("center").showPanel("center1");
32990 layout.endUpdate();
32991 </code></pre>
32992
32993 <b>The container the layout is rendered into can be either the body element or any other element.
32994 If it is not the body element, the container needs to either be an absolute positioned element,
32995 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32996 the container size if it is not the body element.</b>
32997
32998 * @constructor
32999 * Create a new BorderLayout
33000 * @param {String/HTMLElement/Element} container The container this layout is bound to
33001 * @param {Object} config Configuration options
33002  */
33003 Roo.BorderLayout = function(container, config){
33004     config = config || {};
33005     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33006     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33007     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33008         var target = this.factory.validRegions[i];
33009         if(config[target]){
33010             this.addRegion(target, config[target]);
33011         }
33012     }
33013 };
33014
33015 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33016     /**
33017      * Creates and adds a new region if it doesn't already exist.
33018      * @param {String} target The target region key (north, south, east, west or center).
33019      * @param {Object} config The regions config object
33020      * @return {BorderLayoutRegion} The new region
33021      */
33022     addRegion : function(target, config){
33023         if(!this.regions[target]){
33024             var r = this.factory.create(target, this, config);
33025             this.bindRegion(target, r);
33026         }
33027         return this.regions[target];
33028     },
33029
33030     // private (kinda)
33031     bindRegion : function(name, r){
33032         this.regions[name] = r;
33033         r.on("visibilitychange", this.layout, this);
33034         r.on("paneladded", this.layout, this);
33035         r.on("panelremoved", this.layout, this);
33036         r.on("invalidated", this.layout, this);
33037         r.on("resized", this.onRegionResized, this);
33038         r.on("collapsed", this.onRegionCollapsed, this);
33039         r.on("expanded", this.onRegionExpanded, this);
33040     },
33041
33042     /**
33043      * Performs a layout update.
33044      */
33045     layout : function(){
33046         if(this.updating) {
33047             return;
33048         }
33049         var size = this.getViewSize();
33050         var w = size.width;
33051         var h = size.height;
33052         var centerW = w;
33053         var centerH = h;
33054         var centerY = 0;
33055         var centerX = 0;
33056         //var x = 0, y = 0;
33057
33058         var rs = this.regions;
33059         var north = rs["north"];
33060         var south = rs["south"]; 
33061         var west = rs["west"];
33062         var east = rs["east"];
33063         var center = rs["center"];
33064         //if(this.hideOnLayout){ // not supported anymore
33065             //c.el.setStyle("display", "none");
33066         //}
33067         if(north && north.isVisible()){
33068             var b = north.getBox();
33069             var m = north.getMargins();
33070             b.width = w - (m.left+m.right);
33071             b.x = m.left;
33072             b.y = m.top;
33073             centerY = b.height + b.y + m.bottom;
33074             centerH -= centerY;
33075             north.updateBox(this.safeBox(b));
33076         }
33077         if(south && south.isVisible()){
33078             var b = south.getBox();
33079             var m = south.getMargins();
33080             b.width = w - (m.left+m.right);
33081             b.x = m.left;
33082             var totalHeight = (b.height + m.top + m.bottom);
33083             b.y = h - totalHeight + m.top;
33084             centerH -= totalHeight;
33085             south.updateBox(this.safeBox(b));
33086         }
33087         if(west && west.isVisible()){
33088             var b = west.getBox();
33089             var m = west.getMargins();
33090             b.height = centerH - (m.top+m.bottom);
33091             b.x = m.left;
33092             b.y = centerY + m.top;
33093             var totalWidth = (b.width + m.left + m.right);
33094             centerX += totalWidth;
33095             centerW -= totalWidth;
33096             west.updateBox(this.safeBox(b));
33097         }
33098         if(east && east.isVisible()){
33099             var b = east.getBox();
33100             var m = east.getMargins();
33101             b.height = centerH - (m.top+m.bottom);
33102             var totalWidth = (b.width + m.left + m.right);
33103             b.x = w - totalWidth + m.left;
33104             b.y = centerY + m.top;
33105             centerW -= totalWidth;
33106             east.updateBox(this.safeBox(b));
33107         }
33108         if(center){
33109             var m = center.getMargins();
33110             var centerBox = {
33111                 x: centerX + m.left,
33112                 y: centerY + m.top,
33113                 width: centerW - (m.left+m.right),
33114                 height: centerH - (m.top+m.bottom)
33115             };
33116             //if(this.hideOnLayout){
33117                 //center.el.setStyle("display", "block");
33118             //}
33119             center.updateBox(this.safeBox(centerBox));
33120         }
33121         this.el.repaint();
33122         this.fireEvent("layout", this);
33123     },
33124
33125     // private
33126     safeBox : function(box){
33127         box.width = Math.max(0, box.width);
33128         box.height = Math.max(0, box.height);
33129         return box;
33130     },
33131
33132     /**
33133      * Adds a ContentPanel (or subclass) to this layout.
33134      * @param {String} target The target region key (north, south, east, west or center).
33135      * @param {Roo.ContentPanel} panel The panel to add
33136      * @return {Roo.ContentPanel} The added panel
33137      */
33138     add : function(target, panel){
33139          
33140         target = target.toLowerCase();
33141         return this.regions[target].add(panel);
33142     },
33143
33144     /**
33145      * Remove a ContentPanel (or subclass) to this layout.
33146      * @param {String} target The target region key (north, south, east, west or center).
33147      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33148      * @return {Roo.ContentPanel} The removed panel
33149      */
33150     remove : function(target, panel){
33151         target = target.toLowerCase();
33152         return this.regions[target].remove(panel);
33153     },
33154
33155     /**
33156      * Searches all regions for a panel with the specified id
33157      * @param {String} panelId
33158      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33159      */
33160     findPanel : function(panelId){
33161         var rs = this.regions;
33162         for(var target in rs){
33163             if(typeof rs[target] != "function"){
33164                 var p = rs[target].getPanel(panelId);
33165                 if(p){
33166                     return p;
33167                 }
33168             }
33169         }
33170         return null;
33171     },
33172
33173     /**
33174      * Searches all regions for a panel with the specified id and activates (shows) it.
33175      * @param {String/ContentPanel} panelId The panels id or the panel itself
33176      * @return {Roo.ContentPanel} The shown panel or null
33177      */
33178     showPanel : function(panelId) {
33179       var rs = this.regions;
33180       for(var target in rs){
33181          var r = rs[target];
33182          if(typeof r != "function"){
33183             if(r.hasPanel(panelId)){
33184                return r.showPanel(panelId);
33185             }
33186          }
33187       }
33188       return null;
33189    },
33190
33191    /**
33192      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33193      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33194      */
33195     restoreState : function(provider){
33196         if(!provider){
33197             provider = Roo.state.Manager;
33198         }
33199         var sm = new Roo.LayoutStateManager();
33200         sm.init(this, provider);
33201     },
33202
33203     /**
33204      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33205      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33206      * a valid ContentPanel config object.  Example:
33207      * <pre><code>
33208 // Create the main layout
33209 var layout = new Roo.BorderLayout('main-ct', {
33210     west: {
33211         split:true,
33212         minSize: 175,
33213         titlebar: true
33214     },
33215     center: {
33216         title:'Components'
33217     }
33218 }, 'main-ct');
33219
33220 // Create and add multiple ContentPanels at once via configs
33221 layout.batchAdd({
33222    west: {
33223        id: 'source-files',
33224        autoCreate:true,
33225        title:'Ext Source Files',
33226        autoScroll:true,
33227        fitToFrame:true
33228    },
33229    center : {
33230        el: cview,
33231        autoScroll:true,
33232        fitToFrame:true,
33233        toolbar: tb,
33234        resizeEl:'cbody'
33235    }
33236 });
33237 </code></pre>
33238      * @param {Object} regions An object containing ContentPanel configs by region name
33239      */
33240     batchAdd : function(regions){
33241         this.beginUpdate();
33242         for(var rname in regions){
33243             var lr = this.regions[rname];
33244             if(lr){
33245                 this.addTypedPanels(lr, regions[rname]);
33246             }
33247         }
33248         this.endUpdate();
33249     },
33250
33251     // private
33252     addTypedPanels : function(lr, ps){
33253         if(typeof ps == 'string'){
33254             lr.add(new Roo.ContentPanel(ps));
33255         }
33256         else if(ps instanceof Array){
33257             for(var i =0, len = ps.length; i < len; i++){
33258                 this.addTypedPanels(lr, ps[i]);
33259             }
33260         }
33261         else if(!ps.events){ // raw config?
33262             var el = ps.el;
33263             delete ps.el; // prevent conflict
33264             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33265         }
33266         else {  // panel object assumed!
33267             lr.add(ps);
33268         }
33269     },
33270     /**
33271      * Adds a xtype elements to the layout.
33272      * <pre><code>
33273
33274 layout.addxtype({
33275        xtype : 'ContentPanel',
33276        region: 'west',
33277        items: [ .... ]
33278    }
33279 );
33280
33281 layout.addxtype({
33282         xtype : 'NestedLayoutPanel',
33283         region: 'west',
33284         layout: {
33285            center: { },
33286            west: { }   
33287         },
33288         items : [ ... list of content panels or nested layout panels.. ]
33289    }
33290 );
33291 </code></pre>
33292      * @param {Object} cfg Xtype definition of item to add.
33293      */
33294     addxtype : function(cfg)
33295     {
33296         // basically accepts a pannel...
33297         // can accept a layout region..!?!?
33298         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33299         
33300         if (!cfg.xtype.match(/Panel$/)) {
33301             return false;
33302         }
33303         var ret = false;
33304         
33305         if (typeof(cfg.region) == 'undefined') {
33306             Roo.log("Failed to add Panel, region was not set");
33307             Roo.log(cfg);
33308             return false;
33309         }
33310         var region = cfg.region;
33311         delete cfg.region;
33312         
33313           
33314         var xitems = [];
33315         if (cfg.items) {
33316             xitems = cfg.items;
33317             delete cfg.items;
33318         }
33319         var nb = false;
33320         
33321         switch(cfg.xtype) 
33322         {
33323             case 'ContentPanel':  // ContentPanel (el, cfg)
33324             case 'ScrollPanel':  // ContentPanel (el, cfg)
33325             case 'ViewPanel': 
33326                 if(cfg.autoCreate) {
33327                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33328                 } else {
33329                     var el = this.el.createChild();
33330                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33331                 }
33332                 
33333                 this.add(region, ret);
33334                 break;
33335             
33336             
33337             case 'TreePanel': // our new panel!
33338                 cfg.el = this.el.createChild();
33339                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33340                 this.add(region, ret);
33341                 break;
33342             
33343             case 'NestedLayoutPanel': 
33344                 // create a new Layout (which is  a Border Layout...
33345                 var el = this.el.createChild();
33346                 var clayout = cfg.layout;
33347                 delete cfg.layout;
33348                 clayout.items   = clayout.items  || [];
33349                 // replace this exitems with the clayout ones..
33350                 xitems = clayout.items;
33351                  
33352                 
33353                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33354                     cfg.background = false;
33355                 }
33356                 var layout = new Roo.BorderLayout(el, clayout);
33357                 
33358                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33359                 //console.log('adding nested layout panel '  + cfg.toSource());
33360                 this.add(region, ret);
33361                 nb = {}; /// find first...
33362                 break;
33363                 
33364             case 'GridPanel': 
33365             
33366                 // needs grid and region
33367                 
33368                 //var el = this.getRegion(region).el.createChild();
33369                 var el = this.el.createChild();
33370                 // create the grid first...
33371                 
33372                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33373                 delete cfg.grid;
33374                 if (region == 'center' && this.active ) {
33375                     cfg.background = false;
33376                 }
33377                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33378                 
33379                 this.add(region, ret);
33380                 if (cfg.background) {
33381                     ret.on('activate', function(gp) {
33382                         if (!gp.grid.rendered) {
33383                             gp.grid.render();
33384                         }
33385                     });
33386                 } else {
33387                     grid.render();
33388                 }
33389                 break;
33390            
33391            
33392            
33393                 
33394                 
33395                 
33396             default:
33397                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33398                     
33399                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33400                     this.add(region, ret);
33401                 } else {
33402                 
33403                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33404                     return null;
33405                 }
33406                 
33407              // GridPanel (grid, cfg)
33408             
33409         }
33410         this.beginUpdate();
33411         // add children..
33412         var region = '';
33413         var abn = {};
33414         Roo.each(xitems, function(i)  {
33415             region = nb && i.region ? i.region : false;
33416             
33417             var add = ret.addxtype(i);
33418            
33419             if (region) {
33420                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33421                 if (!i.background) {
33422                     abn[region] = nb[region] ;
33423                 }
33424             }
33425             
33426         });
33427         this.endUpdate();
33428
33429         // make the last non-background panel active..
33430         //if (nb) { Roo.log(abn); }
33431         if (nb) {
33432             
33433             for(var r in abn) {
33434                 region = this.getRegion(r);
33435                 if (region) {
33436                     // tried using nb[r], but it does not work..
33437                      
33438                     region.showPanel(abn[r]);
33439                    
33440                 }
33441             }
33442         }
33443         return ret;
33444         
33445     }
33446 });
33447
33448 /**
33449  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33450  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33451  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33452  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33453  * <pre><code>
33454 // shorthand
33455 var CP = Roo.ContentPanel;
33456
33457 var layout = Roo.BorderLayout.create({
33458     north: {
33459         initialSize: 25,
33460         titlebar: false,
33461         panels: [new CP("north", "North")]
33462     },
33463     west: {
33464         split:true,
33465         initialSize: 200,
33466         minSize: 175,
33467         maxSize: 400,
33468         titlebar: true,
33469         collapsible: true,
33470         panels: [new CP("west", {title: "West"})]
33471     },
33472     east: {
33473         split:true,
33474         initialSize: 202,
33475         minSize: 175,
33476         maxSize: 400,
33477         titlebar: true,
33478         collapsible: true,
33479         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33480     },
33481     south: {
33482         split:true,
33483         initialSize: 100,
33484         minSize: 100,
33485         maxSize: 200,
33486         titlebar: true,
33487         collapsible: true,
33488         panels: [new CP("south", {title: "South", closable: true})]
33489     },
33490     center: {
33491         titlebar: true,
33492         autoScroll:true,
33493         resizeTabs: true,
33494         minTabWidth: 50,
33495         preferredTabWidth: 150,
33496         panels: [
33497             new CP("center1", {title: "Close Me", closable: true}),
33498             new CP("center2", {title: "Center Panel", closable: false})
33499         ]
33500     }
33501 }, document.body);
33502
33503 layout.getRegion("center").showPanel("center1");
33504 </code></pre>
33505  * @param config
33506  * @param targetEl
33507  */
33508 Roo.BorderLayout.create = function(config, targetEl){
33509     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33510     layout.beginUpdate();
33511     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33512     for(var j = 0, jlen = regions.length; j < jlen; j++){
33513         var lr = regions[j];
33514         if(layout.regions[lr] && config[lr].panels){
33515             var r = layout.regions[lr];
33516             var ps = config[lr].panels;
33517             layout.addTypedPanels(r, ps);
33518         }
33519     }
33520     layout.endUpdate();
33521     return layout;
33522 };
33523
33524 // private
33525 Roo.BorderLayout.RegionFactory = {
33526     // private
33527     validRegions : ["north","south","east","west","center"],
33528
33529     // private
33530     create : function(target, mgr, config){
33531         target = target.toLowerCase();
33532         if(config.lightweight || config.basic){
33533             return new Roo.BasicLayoutRegion(mgr, config, target);
33534         }
33535         switch(target){
33536             case "north":
33537                 return new Roo.NorthLayoutRegion(mgr, config);
33538             case "south":
33539                 return new Roo.SouthLayoutRegion(mgr, config);
33540             case "east":
33541                 return new Roo.EastLayoutRegion(mgr, config);
33542             case "west":
33543                 return new Roo.WestLayoutRegion(mgr, config);
33544             case "center":
33545                 return new Roo.CenterLayoutRegion(mgr, config);
33546         }
33547         throw 'Layout region "'+target+'" not supported.';
33548     }
33549 };/*
33550  * Based on:
33551  * Ext JS Library 1.1.1
33552  * Copyright(c) 2006-2007, Ext JS, LLC.
33553  *
33554  * Originally Released Under LGPL - original licence link has changed is not relivant.
33555  *
33556  * Fork - LGPL
33557  * <script type="text/javascript">
33558  */
33559  
33560 /**
33561  * @class Roo.BasicLayoutRegion
33562  * @extends Roo.util.Observable
33563  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33564  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33565  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33566  */
33567 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33568     this.mgr = mgr;
33569     this.position  = pos;
33570     this.events = {
33571         /**
33572          * @scope Roo.BasicLayoutRegion
33573          */
33574         
33575         /**
33576          * @event beforeremove
33577          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33578          * @param {Roo.LayoutRegion} this
33579          * @param {Roo.ContentPanel} panel The panel
33580          * @param {Object} e The cancel event object
33581          */
33582         "beforeremove" : true,
33583         /**
33584          * @event invalidated
33585          * Fires when the layout for this region is changed.
33586          * @param {Roo.LayoutRegion} this
33587          */
33588         "invalidated" : true,
33589         /**
33590          * @event visibilitychange
33591          * Fires when this region is shown or hidden 
33592          * @param {Roo.LayoutRegion} this
33593          * @param {Boolean} visibility true or false
33594          */
33595         "visibilitychange" : true,
33596         /**
33597          * @event paneladded
33598          * Fires when a panel is added. 
33599          * @param {Roo.LayoutRegion} this
33600          * @param {Roo.ContentPanel} panel The panel
33601          */
33602         "paneladded" : true,
33603         /**
33604          * @event panelremoved
33605          * Fires when a panel is removed. 
33606          * @param {Roo.LayoutRegion} this
33607          * @param {Roo.ContentPanel} panel The panel
33608          */
33609         "panelremoved" : true,
33610         /**
33611          * @event collapsed
33612          * Fires when this region is collapsed.
33613          * @param {Roo.LayoutRegion} this
33614          */
33615         "collapsed" : true,
33616         /**
33617          * @event expanded
33618          * Fires when this region is expanded.
33619          * @param {Roo.LayoutRegion} this
33620          */
33621         "expanded" : true,
33622         /**
33623          * @event slideshow
33624          * Fires when this region is slid into view.
33625          * @param {Roo.LayoutRegion} this
33626          */
33627         "slideshow" : true,
33628         /**
33629          * @event slidehide
33630          * Fires when this region slides out of view. 
33631          * @param {Roo.LayoutRegion} this
33632          */
33633         "slidehide" : true,
33634         /**
33635          * @event panelactivated
33636          * Fires when a panel is activated. 
33637          * @param {Roo.LayoutRegion} this
33638          * @param {Roo.ContentPanel} panel The activated panel
33639          */
33640         "panelactivated" : true,
33641         /**
33642          * @event resized
33643          * Fires when the user resizes this region. 
33644          * @param {Roo.LayoutRegion} this
33645          * @param {Number} newSize The new size (width for east/west, height for north/south)
33646          */
33647         "resized" : true
33648     };
33649     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33650     this.panels = new Roo.util.MixedCollection();
33651     this.panels.getKey = this.getPanelId.createDelegate(this);
33652     this.box = null;
33653     this.activePanel = null;
33654     // ensure listeners are added...
33655     
33656     if (config.listeners || config.events) {
33657         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33658             listeners : config.listeners || {},
33659             events : config.events || {}
33660         });
33661     }
33662     
33663     if(skipConfig !== true){
33664         this.applyConfig(config);
33665     }
33666 };
33667
33668 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33669     getPanelId : function(p){
33670         return p.getId();
33671     },
33672     
33673     applyConfig : function(config){
33674         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33675         this.config = config;
33676         
33677     },
33678     
33679     /**
33680      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33681      * the width, for horizontal (north, south) the height.
33682      * @param {Number} newSize The new width or height
33683      */
33684     resizeTo : function(newSize){
33685         var el = this.el ? this.el :
33686                  (this.activePanel ? this.activePanel.getEl() : null);
33687         if(el){
33688             switch(this.position){
33689                 case "east":
33690                 case "west":
33691                     el.setWidth(newSize);
33692                     this.fireEvent("resized", this, newSize);
33693                 break;
33694                 case "north":
33695                 case "south":
33696                     el.setHeight(newSize);
33697                     this.fireEvent("resized", this, newSize);
33698                 break;                
33699             }
33700         }
33701     },
33702     
33703     getBox : function(){
33704         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33705     },
33706     
33707     getMargins : function(){
33708         return this.margins;
33709     },
33710     
33711     updateBox : function(box){
33712         this.box = box;
33713         var el = this.activePanel.getEl();
33714         el.dom.style.left = box.x + "px";
33715         el.dom.style.top = box.y + "px";
33716         this.activePanel.setSize(box.width, box.height);
33717     },
33718     
33719     /**
33720      * Returns the container element for this region.
33721      * @return {Roo.Element}
33722      */
33723     getEl : function(){
33724         return this.activePanel;
33725     },
33726     
33727     /**
33728      * Returns true if this region is currently visible.
33729      * @return {Boolean}
33730      */
33731     isVisible : function(){
33732         return this.activePanel ? true : false;
33733     },
33734     
33735     setActivePanel : function(panel){
33736         panel = this.getPanel(panel);
33737         if(this.activePanel && this.activePanel != panel){
33738             this.activePanel.setActiveState(false);
33739             this.activePanel.getEl().setLeftTop(-10000,-10000);
33740         }
33741         this.activePanel = panel;
33742         panel.setActiveState(true);
33743         if(this.box){
33744             panel.setSize(this.box.width, this.box.height);
33745         }
33746         this.fireEvent("panelactivated", this, panel);
33747         this.fireEvent("invalidated");
33748     },
33749     
33750     /**
33751      * Show the specified panel.
33752      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33753      * @return {Roo.ContentPanel} The shown panel or null
33754      */
33755     showPanel : function(panel){
33756         if(panel = this.getPanel(panel)){
33757             this.setActivePanel(panel);
33758         }
33759         return panel;
33760     },
33761     
33762     /**
33763      * Get the active panel for this region.
33764      * @return {Roo.ContentPanel} The active panel or null
33765      */
33766     getActivePanel : function(){
33767         return this.activePanel;
33768     },
33769     
33770     /**
33771      * Add the passed ContentPanel(s)
33772      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33773      * @return {Roo.ContentPanel} The panel added (if only one was added)
33774      */
33775     add : function(panel){
33776         if(arguments.length > 1){
33777             for(var i = 0, len = arguments.length; i < len; i++) {
33778                 this.add(arguments[i]);
33779             }
33780             return null;
33781         }
33782         if(this.hasPanel(panel)){
33783             this.showPanel(panel);
33784             return panel;
33785         }
33786         var el = panel.getEl();
33787         if(el.dom.parentNode != this.mgr.el.dom){
33788             this.mgr.el.dom.appendChild(el.dom);
33789         }
33790         if(panel.setRegion){
33791             panel.setRegion(this);
33792         }
33793         this.panels.add(panel);
33794         el.setStyle("position", "absolute");
33795         if(!panel.background){
33796             this.setActivePanel(panel);
33797             if(this.config.initialSize && this.panels.getCount()==1){
33798                 this.resizeTo(this.config.initialSize);
33799             }
33800         }
33801         this.fireEvent("paneladded", this, panel);
33802         return panel;
33803     },
33804     
33805     /**
33806      * Returns true if the panel is in this region.
33807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33808      * @return {Boolean}
33809      */
33810     hasPanel : function(panel){
33811         if(typeof panel == "object"){ // must be panel obj
33812             panel = panel.getId();
33813         }
33814         return this.getPanel(panel) ? true : false;
33815     },
33816     
33817     /**
33818      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33820      * @param {Boolean} preservePanel Overrides the config preservePanel option
33821      * @return {Roo.ContentPanel} The panel that was removed
33822      */
33823     remove : function(panel, preservePanel){
33824         panel = this.getPanel(panel);
33825         if(!panel){
33826             return null;
33827         }
33828         var e = {};
33829         this.fireEvent("beforeremove", this, panel, e);
33830         if(e.cancel === true){
33831             return null;
33832         }
33833         var panelId = panel.getId();
33834         this.panels.removeKey(panelId);
33835         return panel;
33836     },
33837     
33838     /**
33839      * Returns the panel specified or null if it's not in this region.
33840      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33841      * @return {Roo.ContentPanel}
33842      */
33843     getPanel : function(id){
33844         if(typeof id == "object"){ // must be panel obj
33845             return id;
33846         }
33847         return this.panels.get(id);
33848     },
33849     
33850     /**
33851      * Returns this regions position (north/south/east/west/center).
33852      * @return {String} 
33853      */
33854     getPosition: function(){
33855         return this.position;    
33856     }
33857 });/*
33858  * Based on:
33859  * Ext JS Library 1.1.1
33860  * Copyright(c) 2006-2007, Ext JS, LLC.
33861  *
33862  * Originally Released Under LGPL - original licence link has changed is not relivant.
33863  *
33864  * Fork - LGPL
33865  * <script type="text/javascript">
33866  */
33867  
33868 /**
33869  * @class Roo.LayoutRegion
33870  * @extends Roo.BasicLayoutRegion
33871  * This class represents a region in a layout manager.
33872  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33873  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33874  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33875  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33876  * @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})
33877  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33878  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33879  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33880  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33881  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33882  * @cfg {String}    title           The title for the region (overrides panel titles)
33883  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33884  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33885  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33886  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33887  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33888  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33889  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33890  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33891  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33892  * @cfg {Boolean}   showPin         True to show a pin button
33893  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33894  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33895  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33896  * @cfg {Number}    width           For East/West panels
33897  * @cfg {Number}    height          For North/South panels
33898  * @cfg {Boolean}   split           To show the splitter
33899  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33900  */
33901 Roo.LayoutRegion = function(mgr, config, pos){
33902     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33903     var dh = Roo.DomHelper;
33904     /** This region's container element 
33905     * @type Roo.Element */
33906     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33907     /** This region's title element 
33908     * @type Roo.Element */
33909
33910     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33911         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33912         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33913     ]}, true);
33914     this.titleEl.enableDisplayMode();
33915     /** This region's title text element 
33916     * @type HTMLElement */
33917     this.titleTextEl = this.titleEl.dom.firstChild;
33918     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33919     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33920     this.closeBtn.enableDisplayMode();
33921     this.closeBtn.on("click", this.closeClicked, this);
33922     this.closeBtn.hide();
33923
33924     this.createBody(config);
33925     this.visible = true;
33926     this.collapsed = false;
33927
33928     if(config.hideWhenEmpty){
33929         this.hide();
33930         this.on("paneladded", this.validateVisibility, this);
33931         this.on("panelremoved", this.validateVisibility, this);
33932     }
33933     this.applyConfig(config);
33934 };
33935
33936 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33937
33938     createBody : function(){
33939         /** This region's body element 
33940         * @type Roo.Element */
33941         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33942     },
33943
33944     applyConfig : function(c){
33945         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33946             var dh = Roo.DomHelper;
33947             if(c.titlebar !== false){
33948                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33949                 this.collapseBtn.on("click", this.collapse, this);
33950                 this.collapseBtn.enableDisplayMode();
33951
33952                 if(c.showPin === true || this.showPin){
33953                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33954                     this.stickBtn.enableDisplayMode();
33955                     this.stickBtn.on("click", this.expand, this);
33956                     this.stickBtn.hide();
33957                 }
33958             }
33959             /** This region's collapsed element
33960             * @type Roo.Element */
33961             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33962                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33963             ]}, true);
33964             if(c.floatable !== false){
33965                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33966                this.collapsedEl.on("click", this.collapseClick, this);
33967             }
33968
33969             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33970                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33971                    id: "message", unselectable: "on", style:{"float":"left"}});
33972                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33973              }
33974             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33975             this.expandBtn.on("click", this.expand, this);
33976         }
33977         if(this.collapseBtn){
33978             this.collapseBtn.setVisible(c.collapsible == true);
33979         }
33980         this.cmargins = c.cmargins || this.cmargins ||
33981                          (this.position == "west" || this.position == "east" ?
33982                              {top: 0, left: 2, right:2, bottom: 0} :
33983                              {top: 2, left: 0, right:0, bottom: 2});
33984         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33985         this.bottomTabs = c.tabPosition != "top";
33986         this.autoScroll = c.autoScroll || false;
33987         if(this.autoScroll){
33988             this.bodyEl.setStyle("overflow", "auto");
33989         }else{
33990             this.bodyEl.setStyle("overflow", "hidden");
33991         }
33992         //if(c.titlebar !== false){
33993             if((!c.titlebar && !c.title) || c.titlebar === false){
33994                 this.titleEl.hide();
33995             }else{
33996                 this.titleEl.show();
33997                 if(c.title){
33998                     this.titleTextEl.innerHTML = c.title;
33999                 }
34000             }
34001         //}
34002         this.duration = c.duration || .30;
34003         this.slideDuration = c.slideDuration || .45;
34004         this.config = c;
34005         if(c.collapsed){
34006             this.collapse(true);
34007         }
34008         if(c.hidden){
34009             this.hide();
34010         }
34011     },
34012     /**
34013      * Returns true if this region is currently visible.
34014      * @return {Boolean}
34015      */
34016     isVisible : function(){
34017         return this.visible;
34018     },
34019
34020     /**
34021      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34022      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34023      */
34024     setCollapsedTitle : function(title){
34025         title = title || "&#160;";
34026         if(this.collapsedTitleTextEl){
34027             this.collapsedTitleTextEl.innerHTML = title;
34028         }
34029     },
34030
34031     getBox : function(){
34032         var b;
34033         if(!this.collapsed){
34034             b = this.el.getBox(false, true);
34035         }else{
34036             b = this.collapsedEl.getBox(false, true);
34037         }
34038         return b;
34039     },
34040
34041     getMargins : function(){
34042         return this.collapsed ? this.cmargins : this.margins;
34043     },
34044
34045     highlight : function(){
34046         this.el.addClass("x-layout-panel-dragover");
34047     },
34048
34049     unhighlight : function(){
34050         this.el.removeClass("x-layout-panel-dragover");
34051     },
34052
34053     updateBox : function(box){
34054         this.box = box;
34055         if(!this.collapsed){
34056             this.el.dom.style.left = box.x + "px";
34057             this.el.dom.style.top = box.y + "px";
34058             this.updateBody(box.width, box.height);
34059         }else{
34060             this.collapsedEl.dom.style.left = box.x + "px";
34061             this.collapsedEl.dom.style.top = box.y + "px";
34062             this.collapsedEl.setSize(box.width, box.height);
34063         }
34064         if(this.tabs){
34065             this.tabs.autoSizeTabs();
34066         }
34067     },
34068
34069     updateBody : function(w, h){
34070         if(w !== null){
34071             this.el.setWidth(w);
34072             w -= this.el.getBorderWidth("rl");
34073             if(this.config.adjustments){
34074                 w += this.config.adjustments[0];
34075             }
34076         }
34077         if(h !== null){
34078             this.el.setHeight(h);
34079             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34080             h -= this.el.getBorderWidth("tb");
34081             if(this.config.adjustments){
34082                 h += this.config.adjustments[1];
34083             }
34084             this.bodyEl.setHeight(h);
34085             if(this.tabs){
34086                 h = this.tabs.syncHeight(h);
34087             }
34088         }
34089         if(this.panelSize){
34090             w = w !== null ? w : this.panelSize.width;
34091             h = h !== null ? h : this.panelSize.height;
34092         }
34093         if(this.activePanel){
34094             var el = this.activePanel.getEl();
34095             w = w !== null ? w : el.getWidth();
34096             h = h !== null ? h : el.getHeight();
34097             this.panelSize = {width: w, height: h};
34098             this.activePanel.setSize(w, h);
34099         }
34100         if(Roo.isIE && this.tabs){
34101             this.tabs.el.repaint();
34102         }
34103     },
34104
34105     /**
34106      * Returns the container element for this region.
34107      * @return {Roo.Element}
34108      */
34109     getEl : function(){
34110         return this.el;
34111     },
34112
34113     /**
34114      * Hides this region.
34115      */
34116     hide : function(){
34117         if(!this.collapsed){
34118             this.el.dom.style.left = "-2000px";
34119             this.el.hide();
34120         }else{
34121             this.collapsedEl.dom.style.left = "-2000px";
34122             this.collapsedEl.hide();
34123         }
34124         this.visible = false;
34125         this.fireEvent("visibilitychange", this, false);
34126     },
34127
34128     /**
34129      * Shows this region if it was previously hidden.
34130      */
34131     show : function(){
34132         if(!this.collapsed){
34133             this.el.show();
34134         }else{
34135             this.collapsedEl.show();
34136         }
34137         this.visible = true;
34138         this.fireEvent("visibilitychange", this, true);
34139     },
34140
34141     closeClicked : function(){
34142         if(this.activePanel){
34143             this.remove(this.activePanel);
34144         }
34145     },
34146
34147     collapseClick : function(e){
34148         if(this.isSlid){
34149            e.stopPropagation();
34150            this.slideIn();
34151         }else{
34152            e.stopPropagation();
34153            this.slideOut();
34154         }
34155     },
34156
34157     /**
34158      * Collapses this region.
34159      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34160      */
34161     collapse : function(skipAnim){
34162         if(this.collapsed) {
34163             return;
34164         }
34165         this.collapsed = true;
34166         if(this.split){
34167             this.split.el.hide();
34168         }
34169         if(this.config.animate && skipAnim !== true){
34170             this.fireEvent("invalidated", this);
34171             this.animateCollapse();
34172         }else{
34173             this.el.setLocation(-20000,-20000);
34174             this.el.hide();
34175             this.collapsedEl.show();
34176             this.fireEvent("collapsed", this);
34177             this.fireEvent("invalidated", this);
34178         }
34179     },
34180
34181     animateCollapse : function(){
34182         // overridden
34183     },
34184
34185     /**
34186      * Expands this region if it was previously collapsed.
34187      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34188      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34189      */
34190     expand : function(e, skipAnim){
34191         if(e) {
34192             e.stopPropagation();
34193         }
34194         if(!this.collapsed || this.el.hasActiveFx()) {
34195             return;
34196         }
34197         if(this.isSlid){
34198             this.afterSlideIn();
34199             skipAnim = true;
34200         }
34201         this.collapsed = false;
34202         if(this.config.animate && skipAnim !== true){
34203             this.animateExpand();
34204         }else{
34205             this.el.show();
34206             if(this.split){
34207                 this.split.el.show();
34208             }
34209             this.collapsedEl.setLocation(-2000,-2000);
34210             this.collapsedEl.hide();
34211             this.fireEvent("invalidated", this);
34212             this.fireEvent("expanded", this);
34213         }
34214     },
34215
34216     animateExpand : function(){
34217         // overridden
34218     },
34219
34220     initTabs : function()
34221     {
34222         this.bodyEl.setStyle("overflow", "hidden");
34223         var ts = new Roo.TabPanel(
34224                 this.bodyEl.dom,
34225                 {
34226                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34227                     disableTooltips: this.config.disableTabTips,
34228                     toolbar : this.config.toolbar
34229                 }
34230         );
34231         if(this.config.hideTabs){
34232             ts.stripWrap.setDisplayed(false);
34233         }
34234         this.tabs = ts;
34235         ts.resizeTabs = this.config.resizeTabs === true;
34236         ts.minTabWidth = this.config.minTabWidth || 40;
34237         ts.maxTabWidth = this.config.maxTabWidth || 250;
34238         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34239         ts.monitorResize = false;
34240         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34241         ts.bodyEl.addClass('x-layout-tabs-body');
34242         this.panels.each(this.initPanelAsTab, this);
34243     },
34244
34245     initPanelAsTab : function(panel){
34246         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34247                     this.config.closeOnTab && panel.isClosable());
34248         if(panel.tabTip !== undefined){
34249             ti.setTooltip(panel.tabTip);
34250         }
34251         ti.on("activate", function(){
34252               this.setActivePanel(panel);
34253         }, this);
34254         if(this.config.closeOnTab){
34255             ti.on("beforeclose", function(t, e){
34256                 e.cancel = true;
34257                 this.remove(panel);
34258             }, this);
34259         }
34260         return ti;
34261     },
34262
34263     updatePanelTitle : function(panel, title){
34264         if(this.activePanel == panel){
34265             this.updateTitle(title);
34266         }
34267         if(this.tabs){
34268             var ti = this.tabs.getTab(panel.getEl().id);
34269             ti.setText(title);
34270             if(panel.tabTip !== undefined){
34271                 ti.setTooltip(panel.tabTip);
34272             }
34273         }
34274     },
34275
34276     updateTitle : function(title){
34277         if(this.titleTextEl && !this.config.title){
34278             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34279         }
34280     },
34281
34282     setActivePanel : function(panel){
34283         panel = this.getPanel(panel);
34284         if(this.activePanel && this.activePanel != panel){
34285             this.activePanel.setActiveState(false);
34286         }
34287         this.activePanel = panel;
34288         panel.setActiveState(true);
34289         if(this.panelSize){
34290             panel.setSize(this.panelSize.width, this.panelSize.height);
34291         }
34292         if(this.closeBtn){
34293             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34294         }
34295         this.updateTitle(panel.getTitle());
34296         if(this.tabs){
34297             this.fireEvent("invalidated", this);
34298         }
34299         this.fireEvent("panelactivated", this, panel);
34300     },
34301
34302     /**
34303      * Shows the specified panel.
34304      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34305      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34306      */
34307     showPanel : function(panel)
34308     {
34309         panel = this.getPanel(panel);
34310         if(panel){
34311             if(this.tabs){
34312                 var tab = this.tabs.getTab(panel.getEl().id);
34313                 if(tab.isHidden()){
34314                     this.tabs.unhideTab(tab.id);
34315                 }
34316                 tab.activate();
34317             }else{
34318                 this.setActivePanel(panel);
34319             }
34320         }
34321         return panel;
34322     },
34323
34324     /**
34325      * Get the active panel for this region.
34326      * @return {Roo.ContentPanel} The active panel or null
34327      */
34328     getActivePanel : function(){
34329         return this.activePanel;
34330     },
34331
34332     validateVisibility : function(){
34333         if(this.panels.getCount() < 1){
34334             this.updateTitle("&#160;");
34335             this.closeBtn.hide();
34336             this.hide();
34337         }else{
34338             if(!this.isVisible()){
34339                 this.show();
34340             }
34341         }
34342     },
34343
34344     /**
34345      * Adds the passed ContentPanel(s) to this region.
34346      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34347      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34348      */
34349     add : function(panel){
34350         if(arguments.length > 1){
34351             for(var i = 0, len = arguments.length; i < len; i++) {
34352                 this.add(arguments[i]);
34353             }
34354             return null;
34355         }
34356         if(this.hasPanel(panel)){
34357             this.showPanel(panel);
34358             return panel;
34359         }
34360         panel.setRegion(this);
34361         this.panels.add(panel);
34362         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34363             this.bodyEl.dom.appendChild(panel.getEl().dom);
34364             if(panel.background !== true){
34365                 this.setActivePanel(panel);
34366             }
34367             this.fireEvent("paneladded", this, panel);
34368             return panel;
34369         }
34370         if(!this.tabs){
34371             this.initTabs();
34372         }else{
34373             this.initPanelAsTab(panel);
34374         }
34375         if(panel.background !== true){
34376             this.tabs.activate(panel.getEl().id);
34377         }
34378         this.fireEvent("paneladded", this, panel);
34379         return panel;
34380     },
34381
34382     /**
34383      * Hides the tab for the specified panel.
34384      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34385      */
34386     hidePanel : function(panel){
34387         if(this.tabs && (panel = this.getPanel(panel))){
34388             this.tabs.hideTab(panel.getEl().id);
34389         }
34390     },
34391
34392     /**
34393      * Unhides the tab for a previously hidden panel.
34394      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34395      */
34396     unhidePanel : function(panel){
34397         if(this.tabs && (panel = this.getPanel(panel))){
34398             this.tabs.unhideTab(panel.getEl().id);
34399         }
34400     },
34401
34402     clearPanels : function(){
34403         while(this.panels.getCount() > 0){
34404              this.remove(this.panels.first());
34405         }
34406     },
34407
34408     /**
34409      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34410      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34411      * @param {Boolean} preservePanel Overrides the config preservePanel option
34412      * @return {Roo.ContentPanel} The panel that was removed
34413      */
34414     remove : function(panel, preservePanel){
34415         panel = this.getPanel(panel);
34416         if(!panel){
34417             return null;
34418         }
34419         var e = {};
34420         this.fireEvent("beforeremove", this, panel, e);
34421         if(e.cancel === true){
34422             return null;
34423         }
34424         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34425         var panelId = panel.getId();
34426         this.panels.removeKey(panelId);
34427         if(preservePanel){
34428             document.body.appendChild(panel.getEl().dom);
34429         }
34430         if(this.tabs){
34431             this.tabs.removeTab(panel.getEl().id);
34432         }else if (!preservePanel){
34433             this.bodyEl.dom.removeChild(panel.getEl().dom);
34434         }
34435         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34436             var p = this.panels.first();
34437             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34438             tempEl.appendChild(p.getEl().dom);
34439             this.bodyEl.update("");
34440             this.bodyEl.dom.appendChild(p.getEl().dom);
34441             tempEl = null;
34442             this.updateTitle(p.getTitle());
34443             this.tabs = null;
34444             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34445             this.setActivePanel(p);
34446         }
34447         panel.setRegion(null);
34448         if(this.activePanel == panel){
34449             this.activePanel = null;
34450         }
34451         if(this.config.autoDestroy !== false && preservePanel !== true){
34452             try{panel.destroy();}catch(e){}
34453         }
34454         this.fireEvent("panelremoved", this, panel);
34455         return panel;
34456     },
34457
34458     /**
34459      * Returns the TabPanel component used by this region
34460      * @return {Roo.TabPanel}
34461      */
34462     getTabs : function(){
34463         return this.tabs;
34464     },
34465
34466     createTool : function(parentEl, className){
34467         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34468             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34469         btn.addClassOnOver("x-layout-tools-button-over");
34470         return btn;
34471     }
34472 });/*
34473  * Based on:
34474  * Ext JS Library 1.1.1
34475  * Copyright(c) 2006-2007, Ext JS, LLC.
34476  *
34477  * Originally Released Under LGPL - original licence link has changed is not relivant.
34478  *
34479  * Fork - LGPL
34480  * <script type="text/javascript">
34481  */
34482  
34483
34484
34485 /**
34486  * @class Roo.SplitLayoutRegion
34487  * @extends Roo.LayoutRegion
34488  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34489  */
34490 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34491     this.cursor = cursor;
34492     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34493 };
34494
34495 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34496     splitTip : "Drag to resize.",
34497     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34498     useSplitTips : false,
34499
34500     applyConfig : function(config){
34501         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34502         if(config.split){
34503             if(!this.split){
34504                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34505                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34506                 /** The SplitBar for this region 
34507                 * @type Roo.SplitBar */
34508                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34509                 this.split.on("moved", this.onSplitMove, this);
34510                 this.split.useShim = config.useShim === true;
34511                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34512                 if(this.useSplitTips){
34513                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34514                 }
34515                 if(config.collapsible){
34516                     this.split.el.on("dblclick", this.collapse,  this);
34517                 }
34518             }
34519             if(typeof config.minSize != "undefined"){
34520                 this.split.minSize = config.minSize;
34521             }
34522             if(typeof config.maxSize != "undefined"){
34523                 this.split.maxSize = config.maxSize;
34524             }
34525             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34526                 this.hideSplitter();
34527             }
34528         }
34529     },
34530
34531     getHMaxSize : function(){
34532          var cmax = this.config.maxSize || 10000;
34533          var center = this.mgr.getRegion("center");
34534          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34535     },
34536
34537     getVMaxSize : function(){
34538          var cmax = this.config.maxSize || 10000;
34539          var center = this.mgr.getRegion("center");
34540          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34541     },
34542
34543     onSplitMove : function(split, newSize){
34544         this.fireEvent("resized", this, newSize);
34545     },
34546     
34547     /** 
34548      * Returns the {@link Roo.SplitBar} for this region.
34549      * @return {Roo.SplitBar}
34550      */
34551     getSplitBar : function(){
34552         return this.split;
34553     },
34554     
34555     hide : function(){
34556         this.hideSplitter();
34557         Roo.SplitLayoutRegion.superclass.hide.call(this);
34558     },
34559
34560     hideSplitter : function(){
34561         if(this.split){
34562             this.split.el.setLocation(-2000,-2000);
34563             this.split.el.hide();
34564         }
34565     },
34566
34567     show : function(){
34568         if(this.split){
34569             this.split.el.show();
34570         }
34571         Roo.SplitLayoutRegion.superclass.show.call(this);
34572     },
34573     
34574     beforeSlide: function(){
34575         if(Roo.isGecko){// firefox overflow auto bug workaround
34576             this.bodyEl.clip();
34577             if(this.tabs) {
34578                 this.tabs.bodyEl.clip();
34579             }
34580             if(this.activePanel){
34581                 this.activePanel.getEl().clip();
34582                 
34583                 if(this.activePanel.beforeSlide){
34584                     this.activePanel.beforeSlide();
34585                 }
34586             }
34587         }
34588     },
34589     
34590     afterSlide : function(){
34591         if(Roo.isGecko){// firefox overflow auto bug workaround
34592             this.bodyEl.unclip();
34593             if(this.tabs) {
34594                 this.tabs.bodyEl.unclip();
34595             }
34596             if(this.activePanel){
34597                 this.activePanel.getEl().unclip();
34598                 if(this.activePanel.afterSlide){
34599                     this.activePanel.afterSlide();
34600                 }
34601             }
34602         }
34603     },
34604
34605     initAutoHide : function(){
34606         if(this.autoHide !== false){
34607             if(!this.autoHideHd){
34608                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34609                 this.autoHideHd = {
34610                     "mouseout": function(e){
34611                         if(!e.within(this.el, true)){
34612                             st.delay(500);
34613                         }
34614                     },
34615                     "mouseover" : function(e){
34616                         st.cancel();
34617                     },
34618                     scope : this
34619                 };
34620             }
34621             this.el.on(this.autoHideHd);
34622         }
34623     },
34624
34625     clearAutoHide : function(){
34626         if(this.autoHide !== false){
34627             this.el.un("mouseout", this.autoHideHd.mouseout);
34628             this.el.un("mouseover", this.autoHideHd.mouseover);
34629         }
34630     },
34631
34632     clearMonitor : function(){
34633         Roo.get(document).un("click", this.slideInIf, this);
34634     },
34635
34636     // these names are backwards but not changed for compat
34637     slideOut : function(){
34638         if(this.isSlid || this.el.hasActiveFx()){
34639             return;
34640         }
34641         this.isSlid = true;
34642         if(this.collapseBtn){
34643             this.collapseBtn.hide();
34644         }
34645         this.closeBtnState = this.closeBtn.getStyle('display');
34646         this.closeBtn.hide();
34647         if(this.stickBtn){
34648             this.stickBtn.show();
34649         }
34650         this.el.show();
34651         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34652         this.beforeSlide();
34653         this.el.setStyle("z-index", 10001);
34654         this.el.slideIn(this.getSlideAnchor(), {
34655             callback: function(){
34656                 this.afterSlide();
34657                 this.initAutoHide();
34658                 Roo.get(document).on("click", this.slideInIf, this);
34659                 this.fireEvent("slideshow", this);
34660             },
34661             scope: this,
34662             block: true
34663         });
34664     },
34665
34666     afterSlideIn : function(){
34667         this.clearAutoHide();
34668         this.isSlid = false;
34669         this.clearMonitor();
34670         this.el.setStyle("z-index", "");
34671         if(this.collapseBtn){
34672             this.collapseBtn.show();
34673         }
34674         this.closeBtn.setStyle('display', this.closeBtnState);
34675         if(this.stickBtn){
34676             this.stickBtn.hide();
34677         }
34678         this.fireEvent("slidehide", this);
34679     },
34680
34681     slideIn : function(cb){
34682         if(!this.isSlid || this.el.hasActiveFx()){
34683             Roo.callback(cb);
34684             return;
34685         }
34686         this.isSlid = false;
34687         this.beforeSlide();
34688         this.el.slideOut(this.getSlideAnchor(), {
34689             callback: function(){
34690                 this.el.setLeftTop(-10000, -10000);
34691                 this.afterSlide();
34692                 this.afterSlideIn();
34693                 Roo.callback(cb);
34694             },
34695             scope: this,
34696             block: true
34697         });
34698     },
34699     
34700     slideInIf : function(e){
34701         if(!e.within(this.el)){
34702             this.slideIn();
34703         }
34704     },
34705
34706     animateCollapse : function(){
34707         this.beforeSlide();
34708         this.el.setStyle("z-index", 20000);
34709         var anchor = this.getSlideAnchor();
34710         this.el.slideOut(anchor, {
34711             callback : function(){
34712                 this.el.setStyle("z-index", "");
34713                 this.collapsedEl.slideIn(anchor, {duration:.3});
34714                 this.afterSlide();
34715                 this.el.setLocation(-10000,-10000);
34716                 this.el.hide();
34717                 this.fireEvent("collapsed", this);
34718             },
34719             scope: this,
34720             block: true
34721         });
34722     },
34723
34724     animateExpand : function(){
34725         this.beforeSlide();
34726         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34727         this.el.setStyle("z-index", 20000);
34728         this.collapsedEl.hide({
34729             duration:.1
34730         });
34731         this.el.slideIn(this.getSlideAnchor(), {
34732             callback : function(){
34733                 this.el.setStyle("z-index", "");
34734                 this.afterSlide();
34735                 if(this.split){
34736                     this.split.el.show();
34737                 }
34738                 this.fireEvent("invalidated", this);
34739                 this.fireEvent("expanded", this);
34740             },
34741             scope: this,
34742             block: true
34743         });
34744     },
34745
34746     anchors : {
34747         "west" : "left",
34748         "east" : "right",
34749         "north" : "top",
34750         "south" : "bottom"
34751     },
34752
34753     sanchors : {
34754         "west" : "l",
34755         "east" : "r",
34756         "north" : "t",
34757         "south" : "b"
34758     },
34759
34760     canchors : {
34761         "west" : "tl-tr",
34762         "east" : "tr-tl",
34763         "north" : "tl-bl",
34764         "south" : "bl-tl"
34765     },
34766
34767     getAnchor : function(){
34768         return this.anchors[this.position];
34769     },
34770
34771     getCollapseAnchor : function(){
34772         return this.canchors[this.position];
34773     },
34774
34775     getSlideAnchor : function(){
34776         return this.sanchors[this.position];
34777     },
34778
34779     getAlignAdj : function(){
34780         var cm = this.cmargins;
34781         switch(this.position){
34782             case "west":
34783                 return [0, 0];
34784             break;
34785             case "east":
34786                 return [0, 0];
34787             break;
34788             case "north":
34789                 return [0, 0];
34790             break;
34791             case "south":
34792                 return [0, 0];
34793             break;
34794         }
34795     },
34796
34797     getExpandAdj : function(){
34798         var c = this.collapsedEl, cm = this.cmargins;
34799         switch(this.position){
34800             case "west":
34801                 return [-(cm.right+c.getWidth()+cm.left), 0];
34802             break;
34803             case "east":
34804                 return [cm.right+c.getWidth()+cm.left, 0];
34805             break;
34806             case "north":
34807                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34808             break;
34809             case "south":
34810                 return [0, cm.top+cm.bottom+c.getHeight()];
34811             break;
34812         }
34813     }
34814 });/*
34815  * Based on:
34816  * Ext JS Library 1.1.1
34817  * Copyright(c) 2006-2007, Ext JS, LLC.
34818  *
34819  * Originally Released Under LGPL - original licence link has changed is not relivant.
34820  *
34821  * Fork - LGPL
34822  * <script type="text/javascript">
34823  */
34824 /*
34825  * These classes are private internal classes
34826  */
34827 Roo.CenterLayoutRegion = function(mgr, config){
34828     Roo.LayoutRegion.call(this, mgr, config, "center");
34829     this.visible = true;
34830     this.minWidth = config.minWidth || 20;
34831     this.minHeight = config.minHeight || 20;
34832 };
34833
34834 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34835     hide : function(){
34836         // center panel can't be hidden
34837     },
34838     
34839     show : function(){
34840         // center panel can't be hidden
34841     },
34842     
34843     getMinWidth: function(){
34844         return this.minWidth;
34845     },
34846     
34847     getMinHeight: function(){
34848         return this.minHeight;
34849     }
34850 });
34851
34852
34853 Roo.NorthLayoutRegion = function(mgr, config){
34854     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34855     if(this.split){
34856         this.split.placement = Roo.SplitBar.TOP;
34857         this.split.orientation = Roo.SplitBar.VERTICAL;
34858         this.split.el.addClass("x-layout-split-v");
34859     }
34860     var size = config.initialSize || config.height;
34861     if(typeof size != "undefined"){
34862         this.el.setHeight(size);
34863     }
34864 };
34865 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34866     orientation: Roo.SplitBar.VERTICAL,
34867     getBox : function(){
34868         if(this.collapsed){
34869             return this.collapsedEl.getBox();
34870         }
34871         var box = this.el.getBox();
34872         if(this.split){
34873             box.height += this.split.el.getHeight();
34874         }
34875         return box;
34876     },
34877     
34878     updateBox : function(box){
34879         if(this.split && !this.collapsed){
34880             box.height -= this.split.el.getHeight();
34881             this.split.el.setLeft(box.x);
34882             this.split.el.setTop(box.y+box.height);
34883             this.split.el.setWidth(box.width);
34884         }
34885         if(this.collapsed){
34886             this.updateBody(box.width, null);
34887         }
34888         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34889     }
34890 });
34891
34892 Roo.SouthLayoutRegion = function(mgr, config){
34893     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34894     if(this.split){
34895         this.split.placement = Roo.SplitBar.BOTTOM;
34896         this.split.orientation = Roo.SplitBar.VERTICAL;
34897         this.split.el.addClass("x-layout-split-v");
34898     }
34899     var size = config.initialSize || config.height;
34900     if(typeof size != "undefined"){
34901         this.el.setHeight(size);
34902     }
34903 };
34904 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34905     orientation: Roo.SplitBar.VERTICAL,
34906     getBox : function(){
34907         if(this.collapsed){
34908             return this.collapsedEl.getBox();
34909         }
34910         var box = this.el.getBox();
34911         if(this.split){
34912             var sh = this.split.el.getHeight();
34913             box.height += sh;
34914             box.y -= sh;
34915         }
34916         return box;
34917     },
34918     
34919     updateBox : function(box){
34920         if(this.split && !this.collapsed){
34921             var sh = this.split.el.getHeight();
34922             box.height -= sh;
34923             box.y += sh;
34924             this.split.el.setLeft(box.x);
34925             this.split.el.setTop(box.y-sh);
34926             this.split.el.setWidth(box.width);
34927         }
34928         if(this.collapsed){
34929             this.updateBody(box.width, null);
34930         }
34931         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34932     }
34933 });
34934
34935 Roo.EastLayoutRegion = function(mgr, config){
34936     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34937     if(this.split){
34938         this.split.placement = Roo.SplitBar.RIGHT;
34939         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34940         this.split.el.addClass("x-layout-split-h");
34941     }
34942     var size = config.initialSize || config.width;
34943     if(typeof size != "undefined"){
34944         this.el.setWidth(size);
34945     }
34946 };
34947 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34948     orientation: Roo.SplitBar.HORIZONTAL,
34949     getBox : function(){
34950         if(this.collapsed){
34951             return this.collapsedEl.getBox();
34952         }
34953         var box = this.el.getBox();
34954         if(this.split){
34955             var sw = this.split.el.getWidth();
34956             box.width += sw;
34957             box.x -= sw;
34958         }
34959         return box;
34960     },
34961
34962     updateBox : function(box){
34963         if(this.split && !this.collapsed){
34964             var sw = this.split.el.getWidth();
34965             box.width -= sw;
34966             this.split.el.setLeft(box.x);
34967             this.split.el.setTop(box.y);
34968             this.split.el.setHeight(box.height);
34969             box.x += sw;
34970         }
34971         if(this.collapsed){
34972             this.updateBody(null, box.height);
34973         }
34974         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34975     }
34976 });
34977
34978 Roo.WestLayoutRegion = function(mgr, config){
34979     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34980     if(this.split){
34981         this.split.placement = Roo.SplitBar.LEFT;
34982         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34983         this.split.el.addClass("x-layout-split-h");
34984     }
34985     var size = config.initialSize || config.width;
34986     if(typeof size != "undefined"){
34987         this.el.setWidth(size);
34988     }
34989 };
34990 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34991     orientation: Roo.SplitBar.HORIZONTAL,
34992     getBox : function(){
34993         if(this.collapsed){
34994             return this.collapsedEl.getBox();
34995         }
34996         var box = this.el.getBox();
34997         if(this.split){
34998             box.width += this.split.el.getWidth();
34999         }
35000         return box;
35001     },
35002     
35003     updateBox : function(box){
35004         if(this.split && !this.collapsed){
35005             var sw = this.split.el.getWidth();
35006             box.width -= sw;
35007             this.split.el.setLeft(box.x+box.width);
35008             this.split.el.setTop(box.y);
35009             this.split.el.setHeight(box.height);
35010         }
35011         if(this.collapsed){
35012             this.updateBody(null, box.height);
35013         }
35014         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35015     }
35016 });
35017 /*
35018  * Based on:
35019  * Ext JS Library 1.1.1
35020  * Copyright(c) 2006-2007, Ext JS, LLC.
35021  *
35022  * Originally Released Under LGPL - original licence link has changed is not relivant.
35023  *
35024  * Fork - LGPL
35025  * <script type="text/javascript">
35026  */
35027  
35028  
35029 /*
35030  * Private internal class for reading and applying state
35031  */
35032 Roo.LayoutStateManager = function(layout){
35033      // default empty state
35034      this.state = {
35035         north: {},
35036         south: {},
35037         east: {},
35038         west: {}       
35039     };
35040 };
35041
35042 Roo.LayoutStateManager.prototype = {
35043     init : function(layout, provider){
35044         this.provider = provider;
35045         var state = provider.get(layout.id+"-layout-state");
35046         if(state){
35047             var wasUpdating = layout.isUpdating();
35048             if(!wasUpdating){
35049                 layout.beginUpdate();
35050             }
35051             for(var key in state){
35052                 if(typeof state[key] != "function"){
35053                     var rstate = state[key];
35054                     var r = layout.getRegion(key);
35055                     if(r && rstate){
35056                         if(rstate.size){
35057                             r.resizeTo(rstate.size);
35058                         }
35059                         if(rstate.collapsed == true){
35060                             r.collapse(true);
35061                         }else{
35062                             r.expand(null, true);
35063                         }
35064                     }
35065                 }
35066             }
35067             if(!wasUpdating){
35068                 layout.endUpdate();
35069             }
35070             this.state = state; 
35071         }
35072         this.layout = layout;
35073         layout.on("regionresized", this.onRegionResized, this);
35074         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35075         layout.on("regionexpanded", this.onRegionExpanded, this);
35076     },
35077     
35078     storeState : function(){
35079         this.provider.set(this.layout.id+"-layout-state", this.state);
35080     },
35081     
35082     onRegionResized : function(region, newSize){
35083         this.state[region.getPosition()].size = newSize;
35084         this.storeState();
35085     },
35086     
35087     onRegionCollapsed : function(region){
35088         this.state[region.getPosition()].collapsed = true;
35089         this.storeState();
35090     },
35091     
35092     onRegionExpanded : function(region){
35093         this.state[region.getPosition()].collapsed = false;
35094         this.storeState();
35095     }
35096 };/*
35097  * Based on:
35098  * Ext JS Library 1.1.1
35099  * Copyright(c) 2006-2007, Ext JS, LLC.
35100  *
35101  * Originally Released Under LGPL - original licence link has changed is not relivant.
35102  *
35103  * Fork - LGPL
35104  * <script type="text/javascript">
35105  */
35106 /**
35107  * @class Roo.ContentPanel
35108  * @extends Roo.util.Observable
35109  * A basic ContentPanel element.
35110  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35111  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35112  * @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
35113  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35114  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35115  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35116  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35117  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35118  * @cfg {String} title          The title for this panel
35119  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35120  * @cfg {String} url            Calls {@link #setUrl} with this value
35121  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35122  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35123  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35124  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35125
35126  * @constructor
35127  * Create a new ContentPanel.
35128  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35129  * @param {String/Object} config A string to set only the title or a config object
35130  * @param {String} content (optional) Set the HTML content for this panel
35131  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35132  */
35133 Roo.ContentPanel = function(el, config, content){
35134     
35135      
35136     /*
35137     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35138         config = el;
35139         el = Roo.id();
35140     }
35141     if (config && config.parentLayout) { 
35142         el = config.parentLayout.el.createChild(); 
35143     }
35144     */
35145     if(el.autoCreate){ // xtype is available if this is called from factory
35146         config = el;
35147         el = Roo.id();
35148     }
35149     this.el = Roo.get(el);
35150     if(!this.el && config && config.autoCreate){
35151         if(typeof config.autoCreate == "object"){
35152             if(!config.autoCreate.id){
35153                 config.autoCreate.id = config.id||el;
35154             }
35155             this.el = Roo.DomHelper.append(document.body,
35156                         config.autoCreate, true);
35157         }else{
35158             this.el = Roo.DomHelper.append(document.body,
35159                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35160         }
35161     }
35162     this.closable = false;
35163     this.loaded = false;
35164     this.active = false;
35165     if(typeof config == "string"){
35166         this.title = config;
35167     }else{
35168         Roo.apply(this, config);
35169     }
35170     
35171     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35172         this.wrapEl = this.el.wrap();
35173         this.toolbar.container = this.el.insertSibling(false, 'before');
35174         this.toolbar = new Roo.Toolbar(this.toolbar);
35175     }
35176     
35177     // xtype created footer. - not sure if will work as we normally have to render first..
35178     if (this.footer && !this.footer.el && this.footer.xtype) {
35179         if (!this.wrapEl) {
35180             this.wrapEl = this.el.wrap();
35181         }
35182     
35183         this.footer.container = this.wrapEl.createChild();
35184          
35185         this.footer = Roo.factory(this.footer, Roo);
35186         
35187     }
35188     
35189     if(this.resizeEl){
35190         this.resizeEl = Roo.get(this.resizeEl, true);
35191     }else{
35192         this.resizeEl = this.el;
35193     }
35194     // handle view.xtype
35195     
35196  
35197     
35198     
35199     this.addEvents({
35200         /**
35201          * @event activate
35202          * Fires when this panel is activated. 
35203          * @param {Roo.ContentPanel} this
35204          */
35205         "activate" : true,
35206         /**
35207          * @event deactivate
35208          * Fires when this panel is activated. 
35209          * @param {Roo.ContentPanel} this
35210          */
35211         "deactivate" : true,
35212
35213         /**
35214          * @event resize
35215          * Fires when this panel is resized if fitToFrame is true.
35216          * @param {Roo.ContentPanel} this
35217          * @param {Number} width The width after any component adjustments
35218          * @param {Number} height The height after any component adjustments
35219          */
35220         "resize" : true,
35221         
35222          /**
35223          * @event render
35224          * Fires when this tab is created
35225          * @param {Roo.ContentPanel} this
35226          */
35227         "render" : true
35228         
35229         
35230         
35231     });
35232     
35233
35234     
35235     
35236     if(this.autoScroll){
35237         this.resizeEl.setStyle("overflow", "auto");
35238     } else {
35239         // fix randome scrolling
35240         this.el.on('scroll', function() {
35241             Roo.log('fix random scolling');
35242             this.scrollTo('top',0); 
35243         });
35244     }
35245     content = content || this.content;
35246     if(content){
35247         this.setContent(content);
35248     }
35249     if(config && config.url){
35250         this.setUrl(this.url, this.params, this.loadOnce);
35251     }
35252     
35253     
35254     
35255     Roo.ContentPanel.superclass.constructor.call(this);
35256     
35257     if (this.view && typeof(this.view.xtype) != 'undefined') {
35258         this.view.el = this.el.appendChild(document.createElement("div"));
35259         this.view = Roo.factory(this.view); 
35260         this.view.render  &&  this.view.render(false, '');  
35261     }
35262     
35263     
35264     this.fireEvent('render', this);
35265 };
35266
35267 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35268     tabTip:'',
35269     setRegion : function(region){
35270         this.region = region;
35271         if(region){
35272            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35273         }else{
35274            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35275         } 
35276     },
35277     
35278     /**
35279      * Returns the toolbar for this Panel if one was configured. 
35280      * @return {Roo.Toolbar} 
35281      */
35282     getToolbar : function(){
35283         return this.toolbar;
35284     },
35285     
35286     setActiveState : function(active){
35287         this.active = active;
35288         if(!active){
35289             this.fireEvent("deactivate", this);
35290         }else{
35291             this.fireEvent("activate", this);
35292         }
35293     },
35294     /**
35295      * Updates this panel's element
35296      * @param {String} content The new content
35297      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35298     */
35299     setContent : function(content, loadScripts){
35300         this.el.update(content, loadScripts);
35301     },
35302
35303     ignoreResize : function(w, h){
35304         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35305             return true;
35306         }else{
35307             this.lastSize = {width: w, height: h};
35308             return false;
35309         }
35310     },
35311     /**
35312      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35313      * @return {Roo.UpdateManager} The UpdateManager
35314      */
35315     getUpdateManager : function(){
35316         return this.el.getUpdateManager();
35317     },
35318      /**
35319      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35320      * @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:
35321 <pre><code>
35322 panel.load({
35323     url: "your-url.php",
35324     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35325     callback: yourFunction,
35326     scope: yourObject, //(optional scope)
35327     discardUrl: false,
35328     nocache: false,
35329     text: "Loading...",
35330     timeout: 30,
35331     scripts: false
35332 });
35333 </code></pre>
35334      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35335      * 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.
35336      * @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}
35337      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35338      * @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.
35339      * @return {Roo.ContentPanel} this
35340      */
35341     load : function(){
35342         var um = this.el.getUpdateManager();
35343         um.update.apply(um, arguments);
35344         return this;
35345     },
35346
35347
35348     /**
35349      * 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.
35350      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35351      * @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)
35352      * @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)
35353      * @return {Roo.UpdateManager} The UpdateManager
35354      */
35355     setUrl : function(url, params, loadOnce){
35356         if(this.refreshDelegate){
35357             this.removeListener("activate", this.refreshDelegate);
35358         }
35359         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35360         this.on("activate", this.refreshDelegate);
35361         return this.el.getUpdateManager();
35362     },
35363     
35364     _handleRefresh : function(url, params, loadOnce){
35365         if(!loadOnce || !this.loaded){
35366             var updater = this.el.getUpdateManager();
35367             updater.update(url, params, this._setLoaded.createDelegate(this));
35368         }
35369     },
35370     
35371     _setLoaded : function(){
35372         this.loaded = true;
35373     }, 
35374     
35375     /**
35376      * Returns this panel's id
35377      * @return {String} 
35378      */
35379     getId : function(){
35380         return this.el.id;
35381     },
35382     
35383     /** 
35384      * Returns this panel's element - used by regiosn to add.
35385      * @return {Roo.Element} 
35386      */
35387     getEl : function(){
35388         return this.wrapEl || this.el;
35389     },
35390     
35391     adjustForComponents : function(width, height)
35392     {
35393         //Roo.log('adjustForComponents ');
35394         if(this.resizeEl != this.el){
35395             width -= this.el.getFrameWidth('lr');
35396             height -= this.el.getFrameWidth('tb');
35397         }
35398         if(this.toolbar){
35399             var te = this.toolbar.getEl();
35400             height -= te.getHeight();
35401             te.setWidth(width);
35402         }
35403         if(this.footer){
35404             var te = this.footer.getEl();
35405             Roo.log("footer:" + te.getHeight());
35406             
35407             height -= te.getHeight();
35408             te.setWidth(width);
35409         }
35410         
35411         
35412         if(this.adjustments){
35413             width += this.adjustments[0];
35414             height += this.adjustments[1];
35415         }
35416         return {"width": width, "height": height};
35417     },
35418     
35419     setSize : function(width, height){
35420         if(this.fitToFrame && !this.ignoreResize(width, height)){
35421             if(this.fitContainer && this.resizeEl != this.el){
35422                 this.el.setSize(width, height);
35423             }
35424             var size = this.adjustForComponents(width, height);
35425             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35426             this.fireEvent('resize', this, size.width, size.height);
35427         }
35428     },
35429     
35430     /**
35431      * Returns this panel's title
35432      * @return {String} 
35433      */
35434     getTitle : function(){
35435         return this.title;
35436     },
35437     
35438     /**
35439      * Set this panel's title
35440      * @param {String} title
35441      */
35442     setTitle : function(title){
35443         this.title = title;
35444         if(this.region){
35445             this.region.updatePanelTitle(this, title);
35446         }
35447     },
35448     
35449     /**
35450      * Returns true is this panel was configured to be closable
35451      * @return {Boolean} 
35452      */
35453     isClosable : function(){
35454         return this.closable;
35455     },
35456     
35457     beforeSlide : function(){
35458         this.el.clip();
35459         this.resizeEl.clip();
35460     },
35461     
35462     afterSlide : function(){
35463         this.el.unclip();
35464         this.resizeEl.unclip();
35465     },
35466     
35467     /**
35468      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35469      *   Will fail silently if the {@link #setUrl} method has not been called.
35470      *   This does not activate the panel, just updates its content.
35471      */
35472     refresh : function(){
35473         if(this.refreshDelegate){
35474            this.loaded = false;
35475            this.refreshDelegate();
35476         }
35477     },
35478     
35479     /**
35480      * Destroys this panel
35481      */
35482     destroy : function(){
35483         this.el.removeAllListeners();
35484         var tempEl = document.createElement("span");
35485         tempEl.appendChild(this.el.dom);
35486         tempEl.innerHTML = "";
35487         this.el.remove();
35488         this.el = null;
35489     },
35490     
35491     /**
35492      * form - if the content panel contains a form - this is a reference to it.
35493      * @type {Roo.form.Form}
35494      */
35495     form : false,
35496     /**
35497      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35498      *    This contains a reference to it.
35499      * @type {Roo.View}
35500      */
35501     view : false,
35502     
35503       /**
35504      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35505      * <pre><code>
35506
35507 layout.addxtype({
35508        xtype : 'Form',
35509        items: [ .... ]
35510    }
35511 );
35512
35513 </code></pre>
35514      * @param {Object} cfg Xtype definition of item to add.
35515      */
35516     
35517     addxtype : function(cfg) {
35518         // add form..
35519         if (cfg.xtype.match(/^Form$/)) {
35520             
35521             var el;
35522             //if (this.footer) {
35523             //    el = this.footer.container.insertSibling(false, 'before');
35524             //} else {
35525                 el = this.el.createChild();
35526             //}
35527
35528             this.form = new  Roo.form.Form(cfg);
35529             
35530             
35531             if ( this.form.allItems.length) {
35532                 this.form.render(el.dom);
35533             }
35534             return this.form;
35535         }
35536         // should only have one of theses..
35537         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35538             // views.. should not be just added - used named prop 'view''
35539             
35540             cfg.el = this.el.appendChild(document.createElement("div"));
35541             // factory?
35542             
35543             var ret = new Roo.factory(cfg);
35544              
35545              ret.render && ret.render(false, ''); // render blank..
35546             this.view = ret;
35547             return ret;
35548         }
35549         return false;
35550     }
35551 });
35552
35553 /**
35554  * @class Roo.GridPanel
35555  * @extends Roo.ContentPanel
35556  * @constructor
35557  * Create a new GridPanel.
35558  * @param {Roo.grid.Grid} grid The grid for this panel
35559  * @param {String/Object} config A string to set only the panel's title, or a config object
35560  */
35561 Roo.GridPanel = function(grid, config){
35562     
35563   
35564     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35565         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35566         
35567     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35568     
35569     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35570     
35571     if(this.toolbar){
35572         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35573     }
35574     // xtype created footer. - not sure if will work as we normally have to render first..
35575     if (this.footer && !this.footer.el && this.footer.xtype) {
35576         
35577         this.footer.container = this.grid.getView().getFooterPanel(true);
35578         this.footer.dataSource = this.grid.dataSource;
35579         this.footer = Roo.factory(this.footer, Roo);
35580         
35581     }
35582     
35583     grid.monitorWindowResize = false; // turn off autosizing
35584     grid.autoHeight = false;
35585     grid.autoWidth = false;
35586     this.grid = grid;
35587     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35588 };
35589
35590 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35591     getId : function(){
35592         return this.grid.id;
35593     },
35594     
35595     /**
35596      * Returns the grid for this panel
35597      * @return {Roo.grid.Grid} 
35598      */
35599     getGrid : function(){
35600         return this.grid;    
35601     },
35602     
35603     setSize : function(width, height){
35604         if(!this.ignoreResize(width, height)){
35605             var grid = this.grid;
35606             var size = this.adjustForComponents(width, height);
35607             grid.getGridEl().setSize(size.width, size.height);
35608             grid.autoSize();
35609         }
35610     },
35611     
35612     beforeSlide : function(){
35613         this.grid.getView().scroller.clip();
35614     },
35615     
35616     afterSlide : function(){
35617         this.grid.getView().scroller.unclip();
35618     },
35619     
35620     destroy : function(){
35621         this.grid.destroy();
35622         delete this.grid;
35623         Roo.GridPanel.superclass.destroy.call(this); 
35624     }
35625 });
35626
35627
35628 /**
35629  * @class Roo.NestedLayoutPanel
35630  * @extends Roo.ContentPanel
35631  * @constructor
35632  * Create a new NestedLayoutPanel.
35633  * 
35634  * 
35635  * @param {Roo.BorderLayout} layout The layout for this panel
35636  * @param {String/Object} config A string to set only the title or a config object
35637  */
35638 Roo.NestedLayoutPanel = function(layout, config)
35639 {
35640     // construct with only one argument..
35641     /* FIXME - implement nicer consturctors
35642     if (layout.layout) {
35643         config = layout;
35644         layout = config.layout;
35645         delete config.layout;
35646     }
35647     if (layout.xtype && !layout.getEl) {
35648         // then layout needs constructing..
35649         layout = Roo.factory(layout, Roo);
35650     }
35651     */
35652     
35653     
35654     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35655     
35656     layout.monitorWindowResize = false; // turn off autosizing
35657     this.layout = layout;
35658     this.layout.getEl().addClass("x-layout-nested-layout");
35659     
35660     
35661     
35662     
35663 };
35664
35665 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35666
35667     setSize : function(width, height){
35668         if(!this.ignoreResize(width, height)){
35669             var size = this.adjustForComponents(width, height);
35670             var el = this.layout.getEl();
35671             el.setSize(size.width, size.height);
35672             var touch = el.dom.offsetWidth;
35673             this.layout.layout();
35674             // ie requires a double layout on the first pass
35675             if(Roo.isIE && !this.initialized){
35676                 this.initialized = true;
35677                 this.layout.layout();
35678             }
35679         }
35680     },
35681     
35682     // activate all subpanels if not currently active..
35683     
35684     setActiveState : function(active){
35685         this.active = active;
35686         if(!active){
35687             this.fireEvent("deactivate", this);
35688             return;
35689         }
35690         
35691         this.fireEvent("activate", this);
35692         // not sure if this should happen before or after..
35693         if (!this.layout) {
35694             return; // should not happen..
35695         }
35696         var reg = false;
35697         for (var r in this.layout.regions) {
35698             reg = this.layout.getRegion(r);
35699             if (reg.getActivePanel()) {
35700                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35701                 reg.setActivePanel(reg.getActivePanel());
35702                 continue;
35703             }
35704             if (!reg.panels.length) {
35705                 continue;
35706             }
35707             reg.showPanel(reg.getPanel(0));
35708         }
35709         
35710         
35711         
35712         
35713     },
35714     
35715     /**
35716      * Returns the nested BorderLayout for this panel
35717      * @return {Roo.BorderLayout} 
35718      */
35719     getLayout : function(){
35720         return this.layout;
35721     },
35722     
35723      /**
35724      * Adds a xtype elements to the layout of the nested panel
35725      * <pre><code>
35726
35727 panel.addxtype({
35728        xtype : 'ContentPanel',
35729        region: 'west',
35730        items: [ .... ]
35731    }
35732 );
35733
35734 panel.addxtype({
35735         xtype : 'NestedLayoutPanel',
35736         region: 'west',
35737         layout: {
35738            center: { },
35739            west: { }   
35740         },
35741         items : [ ... list of content panels or nested layout panels.. ]
35742    }
35743 );
35744 </code></pre>
35745      * @param {Object} cfg Xtype definition of item to add.
35746      */
35747     addxtype : function(cfg) {
35748         return this.layout.addxtype(cfg);
35749     
35750     }
35751 });
35752
35753 Roo.ScrollPanel = function(el, config, content){
35754     config = config || {};
35755     config.fitToFrame = true;
35756     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35757     
35758     this.el.dom.style.overflow = "hidden";
35759     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35760     this.el.removeClass("x-layout-inactive-content");
35761     this.el.on("mousewheel", this.onWheel, this);
35762
35763     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35764     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35765     up.unselectable(); down.unselectable();
35766     up.on("click", this.scrollUp, this);
35767     down.on("click", this.scrollDown, this);
35768     up.addClassOnOver("x-scroller-btn-over");
35769     down.addClassOnOver("x-scroller-btn-over");
35770     up.addClassOnClick("x-scroller-btn-click");
35771     down.addClassOnClick("x-scroller-btn-click");
35772     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35773
35774     this.resizeEl = this.el;
35775     this.el = wrap; this.up = up; this.down = down;
35776 };
35777
35778 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35779     increment : 100,
35780     wheelIncrement : 5,
35781     scrollUp : function(){
35782         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35783     },
35784
35785     scrollDown : function(){
35786         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35787     },
35788
35789     afterScroll : function(){
35790         var el = this.resizeEl;
35791         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35792         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35793         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35794     },
35795
35796     setSize : function(){
35797         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35798         this.afterScroll();
35799     },
35800
35801     onWheel : function(e){
35802         var d = e.getWheelDelta();
35803         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35804         this.afterScroll();
35805         e.stopEvent();
35806     },
35807
35808     setContent : function(content, loadScripts){
35809         this.resizeEl.update(content, loadScripts);
35810     }
35811
35812 });
35813
35814
35815
35816
35817
35818
35819
35820
35821
35822 /**
35823  * @class Roo.TreePanel
35824  * @extends Roo.ContentPanel
35825  * @constructor
35826  * Create a new TreePanel. - defaults to fit/scoll contents.
35827  * @param {String/Object} config A string to set only the panel's title, or a config object
35828  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35829  */
35830 Roo.TreePanel = function(config){
35831     var el = config.el;
35832     var tree = config.tree;
35833     delete config.tree; 
35834     delete config.el; // hopefull!
35835     
35836     // wrapper for IE7 strict & safari scroll issue
35837     
35838     var treeEl = el.createChild();
35839     config.resizeEl = treeEl;
35840     
35841     
35842     
35843     Roo.TreePanel.superclass.constructor.call(this, el, config);
35844  
35845  
35846     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35847     //console.log(tree);
35848     this.on('activate', function()
35849     {
35850         if (this.tree.rendered) {
35851             return;
35852         }
35853         //console.log('render tree');
35854         this.tree.render();
35855     });
35856     // this should not be needed.. - it's actually the 'el' that resizes?
35857     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35858     
35859     //this.on('resize',  function (cp, w, h) {
35860     //        this.tree.innerCt.setWidth(w);
35861     //        this.tree.innerCt.setHeight(h);
35862     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35863     //});
35864
35865         
35866     
35867 };
35868
35869 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35870     fitToFrame : true,
35871     autoScroll : true
35872 });
35873
35874
35875
35876
35877
35878
35879
35880
35881
35882
35883
35884 /*
35885  * Based on:
35886  * Ext JS Library 1.1.1
35887  * Copyright(c) 2006-2007, Ext JS, LLC.
35888  *
35889  * Originally Released Under LGPL - original licence link has changed is not relivant.
35890  *
35891  * Fork - LGPL
35892  * <script type="text/javascript">
35893  */
35894  
35895
35896 /**
35897  * @class Roo.ReaderLayout
35898  * @extends Roo.BorderLayout
35899  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35900  * center region containing two nested regions (a top one for a list view and one for item preview below),
35901  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35902  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35903  * expedites the setup of the overall layout and regions for this common application style.
35904  * Example:
35905  <pre><code>
35906 var reader = new Roo.ReaderLayout();
35907 var CP = Roo.ContentPanel;  // shortcut for adding
35908
35909 reader.beginUpdate();
35910 reader.add("north", new CP("north", "North"));
35911 reader.add("west", new CP("west", {title: "West"}));
35912 reader.add("east", new CP("east", {title: "East"}));
35913
35914 reader.regions.listView.add(new CP("listView", "List"));
35915 reader.regions.preview.add(new CP("preview", "Preview"));
35916 reader.endUpdate();
35917 </code></pre>
35918 * @constructor
35919 * Create a new ReaderLayout
35920 * @param {Object} config Configuration options
35921 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35922 * document.body if omitted)
35923 */
35924 Roo.ReaderLayout = function(config, renderTo){
35925     var c = config || {size:{}};
35926     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35927         north: c.north !== false ? Roo.apply({
35928             split:false,
35929             initialSize: 32,
35930             titlebar: false
35931         }, c.north) : false,
35932         west: c.west !== false ? Roo.apply({
35933             split:true,
35934             initialSize: 200,
35935             minSize: 175,
35936             maxSize: 400,
35937             titlebar: true,
35938             collapsible: true,
35939             animate: true,
35940             margins:{left:5,right:0,bottom:5,top:5},
35941             cmargins:{left:5,right:5,bottom:5,top:5}
35942         }, c.west) : false,
35943         east: c.east !== false ? Roo.apply({
35944             split:true,
35945             initialSize: 200,
35946             minSize: 175,
35947             maxSize: 400,
35948             titlebar: true,
35949             collapsible: true,
35950             animate: true,
35951             margins:{left:0,right:5,bottom:5,top:5},
35952             cmargins:{left:5,right:5,bottom:5,top:5}
35953         }, c.east) : false,
35954         center: Roo.apply({
35955             tabPosition: 'top',
35956             autoScroll:false,
35957             closeOnTab: true,
35958             titlebar:false,
35959             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35960         }, c.center)
35961     });
35962
35963     this.el.addClass('x-reader');
35964
35965     this.beginUpdate();
35966
35967     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35968         south: c.preview !== false ? Roo.apply({
35969             split:true,
35970             initialSize: 200,
35971             minSize: 100,
35972             autoScroll:true,
35973             collapsible:true,
35974             titlebar: true,
35975             cmargins:{top:5,left:0, right:0, bottom:0}
35976         }, c.preview) : false,
35977         center: Roo.apply({
35978             autoScroll:false,
35979             titlebar:false,
35980             minHeight:200
35981         }, c.listView)
35982     });
35983     this.add('center', new Roo.NestedLayoutPanel(inner,
35984             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35985
35986     this.endUpdate();
35987
35988     this.regions.preview = inner.getRegion('south');
35989     this.regions.listView = inner.getRegion('center');
35990 };
35991
35992 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35993  * Based on:
35994  * Ext JS Library 1.1.1
35995  * Copyright(c) 2006-2007, Ext JS, LLC.
35996  *
35997  * Originally Released Under LGPL - original licence link has changed is not relivant.
35998  *
35999  * Fork - LGPL
36000  * <script type="text/javascript">
36001  */
36002  
36003 /**
36004  * @class Roo.grid.Grid
36005  * @extends Roo.util.Observable
36006  * This class represents the primary interface of a component based grid control.
36007  * <br><br>Usage:<pre><code>
36008  var grid = new Roo.grid.Grid("my-container-id", {
36009      ds: myDataStore,
36010      cm: myColModel,
36011      selModel: mySelectionModel,
36012      autoSizeColumns: true,
36013      monitorWindowResize: false,
36014      trackMouseOver: true
36015  });
36016  // set any options
36017  grid.render();
36018  * </code></pre>
36019  * <b>Common Problems:</b><br/>
36020  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36021  * element will correct this<br/>
36022  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36023  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36024  * are unpredictable.<br/>
36025  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36026  * grid to calculate dimensions/offsets.<br/>
36027   * @constructor
36028  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36029  * The container MUST have some type of size defined for the grid to fill. The container will be
36030  * automatically set to position relative if it isn't already.
36031  * @param {Object} config A config object that sets properties on this grid.
36032  */
36033 Roo.grid.Grid = function(container, config){
36034         // initialize the container
36035         this.container = Roo.get(container);
36036         this.container.update("");
36037         this.container.setStyle("overflow", "hidden");
36038     this.container.addClass('x-grid-container');
36039
36040     this.id = this.container.id;
36041
36042     Roo.apply(this, config);
36043     // check and correct shorthanded configs
36044     if(this.ds){
36045         this.dataSource = this.ds;
36046         delete this.ds;
36047     }
36048     if(this.cm){
36049         this.colModel = this.cm;
36050         delete this.cm;
36051     }
36052     if(this.sm){
36053         this.selModel = this.sm;
36054         delete this.sm;
36055     }
36056
36057     if (this.selModel) {
36058         this.selModel = Roo.factory(this.selModel, Roo.grid);
36059         this.sm = this.selModel;
36060         this.sm.xmodule = this.xmodule || false;
36061     }
36062     if (typeof(this.colModel.config) == 'undefined') {
36063         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36064         this.cm = this.colModel;
36065         this.cm.xmodule = this.xmodule || false;
36066     }
36067     if (this.dataSource) {
36068         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36069         this.ds = this.dataSource;
36070         this.ds.xmodule = this.xmodule || false;
36071          
36072     }
36073     
36074     
36075     
36076     if(this.width){
36077         this.container.setWidth(this.width);
36078     }
36079
36080     if(this.height){
36081         this.container.setHeight(this.height);
36082     }
36083     /** @private */
36084         this.addEvents({
36085         // raw events
36086         /**
36087          * @event click
36088          * The raw click event for the entire grid.
36089          * @param {Roo.EventObject} e
36090          */
36091         "click" : true,
36092         /**
36093          * @event dblclick
36094          * The raw dblclick event for the entire grid.
36095          * @param {Roo.EventObject} e
36096          */
36097         "dblclick" : true,
36098         /**
36099          * @event contextmenu
36100          * The raw contextmenu event for the entire grid.
36101          * @param {Roo.EventObject} e
36102          */
36103         "contextmenu" : true,
36104         /**
36105          * @event mousedown
36106          * The raw mousedown event for the entire grid.
36107          * @param {Roo.EventObject} e
36108          */
36109         "mousedown" : true,
36110         /**
36111          * @event mouseup
36112          * The raw mouseup event for the entire grid.
36113          * @param {Roo.EventObject} e
36114          */
36115         "mouseup" : true,
36116         /**
36117          * @event mouseover
36118          * The raw mouseover event for the entire grid.
36119          * @param {Roo.EventObject} e
36120          */
36121         "mouseover" : true,
36122         /**
36123          * @event mouseout
36124          * The raw mouseout event for the entire grid.
36125          * @param {Roo.EventObject} e
36126          */
36127         "mouseout" : true,
36128         /**
36129          * @event keypress
36130          * The raw keypress event for the entire grid.
36131          * @param {Roo.EventObject} e
36132          */
36133         "keypress" : true,
36134         /**
36135          * @event keydown
36136          * The raw keydown event for the entire grid.
36137          * @param {Roo.EventObject} e
36138          */
36139         "keydown" : true,
36140
36141         // custom events
36142
36143         /**
36144          * @event cellclick
36145          * Fires when a cell is clicked
36146          * @param {Grid} this
36147          * @param {Number} rowIndex
36148          * @param {Number} columnIndex
36149          * @param {Roo.EventObject} e
36150          */
36151         "cellclick" : true,
36152         /**
36153          * @event celldblclick
36154          * Fires when a cell is double clicked
36155          * @param {Grid} this
36156          * @param {Number} rowIndex
36157          * @param {Number} columnIndex
36158          * @param {Roo.EventObject} e
36159          */
36160         "celldblclick" : true,
36161         /**
36162          * @event rowclick
36163          * Fires when a row is clicked
36164          * @param {Grid} this
36165          * @param {Number} rowIndex
36166          * @param {Roo.EventObject} e
36167          */
36168         "rowclick" : true,
36169         /**
36170          * @event rowdblclick
36171          * Fires when a row is double clicked
36172          * @param {Grid} this
36173          * @param {Number} rowIndex
36174          * @param {Roo.EventObject} e
36175          */
36176         "rowdblclick" : true,
36177         /**
36178          * @event headerclick
36179          * Fires when a header is clicked
36180          * @param {Grid} this
36181          * @param {Number} columnIndex
36182          * @param {Roo.EventObject} e
36183          */
36184         "headerclick" : true,
36185         /**
36186          * @event headerdblclick
36187          * Fires when a header cell is double clicked
36188          * @param {Grid} this
36189          * @param {Number} columnIndex
36190          * @param {Roo.EventObject} e
36191          */
36192         "headerdblclick" : true,
36193         /**
36194          * @event rowcontextmenu
36195          * Fires when a row is right clicked
36196          * @param {Grid} this
36197          * @param {Number} rowIndex
36198          * @param {Roo.EventObject} e
36199          */
36200         "rowcontextmenu" : true,
36201         /**
36202          * @event cellcontextmenu
36203          * Fires when a cell is right clicked
36204          * @param {Grid} this
36205          * @param {Number} rowIndex
36206          * @param {Number} cellIndex
36207          * @param {Roo.EventObject} e
36208          */
36209          "cellcontextmenu" : true,
36210         /**
36211          * @event headercontextmenu
36212          * Fires when a header is right clicked
36213          * @param {Grid} this
36214          * @param {Number} columnIndex
36215          * @param {Roo.EventObject} e
36216          */
36217         "headercontextmenu" : true,
36218         /**
36219          * @event bodyscroll
36220          * Fires when the body element is scrolled
36221          * @param {Number} scrollLeft
36222          * @param {Number} scrollTop
36223          */
36224         "bodyscroll" : true,
36225         /**
36226          * @event columnresize
36227          * Fires when the user resizes a column
36228          * @param {Number} columnIndex
36229          * @param {Number} newSize
36230          */
36231         "columnresize" : true,
36232         /**
36233          * @event columnmove
36234          * Fires when the user moves a column
36235          * @param {Number} oldIndex
36236          * @param {Number} newIndex
36237          */
36238         "columnmove" : true,
36239         /**
36240          * @event startdrag
36241          * Fires when row(s) start being dragged
36242          * @param {Grid} this
36243          * @param {Roo.GridDD} dd The drag drop object
36244          * @param {event} e The raw browser event
36245          */
36246         "startdrag" : true,
36247         /**
36248          * @event enddrag
36249          * Fires when a drag operation is complete
36250          * @param {Grid} this
36251          * @param {Roo.GridDD} dd The drag drop object
36252          * @param {event} e The raw browser event
36253          */
36254         "enddrag" : true,
36255         /**
36256          * @event dragdrop
36257          * Fires when dragged row(s) are dropped on a valid DD target
36258          * @param {Grid} this
36259          * @param {Roo.GridDD} dd The drag drop object
36260          * @param {String} targetId The target drag drop object
36261          * @param {event} e The raw browser event
36262          */
36263         "dragdrop" : true,
36264         /**
36265          * @event dragover
36266          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36267          * @param {Grid} this
36268          * @param {Roo.GridDD} dd The drag drop object
36269          * @param {String} targetId The target drag drop object
36270          * @param {event} e The raw browser event
36271          */
36272         "dragover" : true,
36273         /**
36274          * @event dragenter
36275          *  Fires when the dragged row(s) first cross another DD target while being dragged
36276          * @param {Grid} this
36277          * @param {Roo.GridDD} dd The drag drop object
36278          * @param {String} targetId The target drag drop object
36279          * @param {event} e The raw browser event
36280          */
36281         "dragenter" : true,
36282         /**
36283          * @event dragout
36284          * Fires when the dragged row(s) leave another DD target while being dragged
36285          * @param {Grid} this
36286          * @param {Roo.GridDD} dd The drag drop object
36287          * @param {String} targetId The target drag drop object
36288          * @param {event} e The raw browser event
36289          */
36290         "dragout" : true,
36291         /**
36292          * @event rowclass
36293          * Fires when a row is rendered, so you can change add a style to it.
36294          * @param {GridView} gridview   The grid view
36295          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36296          */
36297         'rowclass' : true,
36298
36299         /**
36300          * @event render
36301          * Fires when the grid is rendered
36302          * @param {Grid} grid
36303          */
36304         'render' : true
36305     });
36306
36307     Roo.grid.Grid.superclass.constructor.call(this);
36308 };
36309 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36310     
36311     /**
36312      * @cfg {String} ddGroup - drag drop group.
36313      */
36314
36315     /**
36316      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36317      */
36318     minColumnWidth : 25,
36319
36320     /**
36321      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36322      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36323      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36324      */
36325     autoSizeColumns : false,
36326
36327     /**
36328      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36329      */
36330     autoSizeHeaders : true,
36331
36332     /**
36333      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36334      */
36335     monitorWindowResize : true,
36336
36337     /**
36338      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36339      * rows measured to get a columns size. Default is 0 (all rows).
36340      */
36341     maxRowsToMeasure : 0,
36342
36343     /**
36344      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36345      */
36346     trackMouseOver : true,
36347
36348     /**
36349     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36350     */
36351     
36352     /**
36353     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36354     */
36355     enableDragDrop : false,
36356     
36357     /**
36358     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36359     */
36360     enableColumnMove : true,
36361     
36362     /**
36363     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36364     */
36365     enableColumnHide : true,
36366     
36367     /**
36368     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36369     */
36370     enableRowHeightSync : false,
36371     
36372     /**
36373     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36374     */
36375     stripeRows : true,
36376     
36377     /**
36378     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36379     */
36380     autoHeight : false,
36381
36382     /**
36383      * @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.
36384      */
36385     autoExpandColumn : false,
36386
36387     /**
36388     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36389     * Default is 50.
36390     */
36391     autoExpandMin : 50,
36392
36393     /**
36394     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36395     */
36396     autoExpandMax : 1000,
36397
36398     /**
36399     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36400     */
36401     view : null,
36402
36403     /**
36404     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36405     */
36406     loadMask : false,
36407     /**
36408     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36409     */
36410     dropTarget: false,
36411     
36412    
36413     
36414     // private
36415     rendered : false,
36416
36417     /**
36418     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36419     * of a fixed width. Default is false.
36420     */
36421     /**
36422     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36423     */
36424     /**
36425      * Called once after all setup has been completed and the grid is ready to be rendered.
36426      * @return {Roo.grid.Grid} this
36427      */
36428     render : function()
36429     {
36430         var c = this.container;
36431         // try to detect autoHeight/width mode
36432         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36433             this.autoHeight = true;
36434         }
36435         var view = this.getView();
36436         view.init(this);
36437
36438         c.on("click", this.onClick, this);
36439         c.on("dblclick", this.onDblClick, this);
36440         c.on("contextmenu", this.onContextMenu, this);
36441         c.on("keydown", this.onKeyDown, this);
36442         if (Roo.isTouch) {
36443             c.on("touchstart", this.onTouchStart, this);
36444         }
36445
36446         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36447
36448         this.getSelectionModel().init(this);
36449
36450         view.render();
36451
36452         if(this.loadMask){
36453             this.loadMask = new Roo.LoadMask(this.container,
36454                     Roo.apply({store:this.dataSource}, this.loadMask));
36455         }
36456         
36457         
36458         if (this.toolbar && this.toolbar.xtype) {
36459             this.toolbar.container = this.getView().getHeaderPanel(true);
36460             this.toolbar = new Roo.Toolbar(this.toolbar);
36461         }
36462         if (this.footer && this.footer.xtype) {
36463             this.footer.dataSource = this.getDataSource();
36464             this.footer.container = this.getView().getFooterPanel(true);
36465             this.footer = Roo.factory(this.footer, Roo);
36466         }
36467         if (this.dropTarget && this.dropTarget.xtype) {
36468             delete this.dropTarget.xtype;
36469             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36470         }
36471         
36472         
36473         this.rendered = true;
36474         this.fireEvent('render', this);
36475         return this;
36476     },
36477
36478         /**
36479          * Reconfigures the grid to use a different Store and Column Model.
36480          * The View will be bound to the new objects and refreshed.
36481          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36482          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36483          */
36484     reconfigure : function(dataSource, colModel){
36485         if(this.loadMask){
36486             this.loadMask.destroy();
36487             this.loadMask = new Roo.LoadMask(this.container,
36488                     Roo.apply({store:dataSource}, this.loadMask));
36489         }
36490         this.view.bind(dataSource, colModel);
36491         this.dataSource = dataSource;
36492         this.colModel = colModel;
36493         this.view.refresh(true);
36494     },
36495
36496     // private
36497     onKeyDown : function(e){
36498         this.fireEvent("keydown", e);
36499     },
36500
36501     /**
36502      * Destroy this grid.
36503      * @param {Boolean} removeEl True to remove the element
36504      */
36505     destroy : function(removeEl, keepListeners){
36506         if(this.loadMask){
36507             this.loadMask.destroy();
36508         }
36509         var c = this.container;
36510         c.removeAllListeners();
36511         this.view.destroy();
36512         this.colModel.purgeListeners();
36513         if(!keepListeners){
36514             this.purgeListeners();
36515         }
36516         c.update("");
36517         if(removeEl === true){
36518             c.remove();
36519         }
36520     },
36521
36522     // private
36523     processEvent : function(name, e){
36524         // does this fire select???
36525         //Roo.log('grid:processEvent '  + name);
36526         
36527         if (name != 'touchstart' ) {
36528             this.fireEvent(name, e);    
36529         }
36530         
36531         var t = e.getTarget();
36532         var v = this.view;
36533         var header = v.findHeaderIndex(t);
36534         if(header !== false){
36535             var ename = name == 'touchstart' ? 'click' : name;
36536              
36537             this.fireEvent("header" + ename, this, header, e);
36538         }else{
36539             var row = v.findRowIndex(t);
36540             var cell = v.findCellIndex(t);
36541             if (name == 'touchstart') {
36542                 // first touch is always a click.
36543                 // hopefull this happens after selection is updated.?
36544                 name = false;
36545                 
36546                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36547                     var cs = this.selModel.getSelectedCell();
36548                     if (row == cs[0] && cell == cs[1]){
36549                         name = 'dblclick';
36550                     }
36551                 }
36552                 if (typeof(this.selModel.getSelections) != 'undefined') {
36553                     var cs = this.selModel.getSelections();
36554                     var ds = this.dataSource;
36555                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36556                         name = 'dblclick';
36557                     }
36558                 }
36559                 if (!name) {
36560                     return;
36561                 }
36562             }
36563             
36564             
36565             if(row !== false){
36566                 this.fireEvent("row" + name, this, row, e);
36567                 if(cell !== false){
36568                     this.fireEvent("cell" + name, this, row, cell, e);
36569                 }
36570             }
36571         }
36572     },
36573
36574     // private
36575     onClick : function(e){
36576         this.processEvent("click", e);
36577     },
36578    // private
36579     onTouchStart : function(e){
36580         this.processEvent("touchstart", e);
36581     },
36582
36583     // private
36584     onContextMenu : function(e, t){
36585         this.processEvent("contextmenu", e);
36586     },
36587
36588     // private
36589     onDblClick : function(e){
36590         this.processEvent("dblclick", e);
36591     },
36592
36593     // private
36594     walkCells : function(row, col, step, fn, scope){
36595         var cm = this.colModel, clen = cm.getColumnCount();
36596         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36597         if(step < 0){
36598             if(col < 0){
36599                 row--;
36600                 first = false;
36601             }
36602             while(row >= 0){
36603                 if(!first){
36604                     col = clen-1;
36605                 }
36606                 first = false;
36607                 while(col >= 0){
36608                     if(fn.call(scope || this, row, col, cm) === true){
36609                         return [row, col];
36610                     }
36611                     col--;
36612                 }
36613                 row--;
36614             }
36615         } else {
36616             if(col >= clen){
36617                 row++;
36618                 first = false;
36619             }
36620             while(row < rlen){
36621                 if(!first){
36622                     col = 0;
36623                 }
36624                 first = false;
36625                 while(col < clen){
36626                     if(fn.call(scope || this, row, col, cm) === true){
36627                         return [row, col];
36628                     }
36629                     col++;
36630                 }
36631                 row++;
36632             }
36633         }
36634         return null;
36635     },
36636
36637     // private
36638     getSelections : function(){
36639         return this.selModel.getSelections();
36640     },
36641
36642     /**
36643      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36644      * but if manual update is required this method will initiate it.
36645      */
36646     autoSize : function(){
36647         if(this.rendered){
36648             this.view.layout();
36649             if(this.view.adjustForScroll){
36650                 this.view.adjustForScroll();
36651             }
36652         }
36653     },
36654
36655     /**
36656      * Returns the grid's underlying element.
36657      * @return {Element} The element
36658      */
36659     getGridEl : function(){
36660         return this.container;
36661     },
36662
36663     // private for compatibility, overridden by editor grid
36664     stopEditing : function(){},
36665
36666     /**
36667      * Returns the grid's SelectionModel.
36668      * @return {SelectionModel}
36669      */
36670     getSelectionModel : function(){
36671         if(!this.selModel){
36672             this.selModel = new Roo.grid.RowSelectionModel();
36673         }
36674         return this.selModel;
36675     },
36676
36677     /**
36678      * Returns the grid's DataSource.
36679      * @return {DataSource}
36680      */
36681     getDataSource : function(){
36682         return this.dataSource;
36683     },
36684
36685     /**
36686      * Returns the grid's ColumnModel.
36687      * @return {ColumnModel}
36688      */
36689     getColumnModel : function(){
36690         return this.colModel;
36691     },
36692
36693     /**
36694      * Returns the grid's GridView object.
36695      * @return {GridView}
36696      */
36697     getView : function(){
36698         if(!this.view){
36699             this.view = new Roo.grid.GridView(this.viewConfig);
36700         }
36701         return this.view;
36702     },
36703     /**
36704      * Called to get grid's drag proxy text, by default returns this.ddText.
36705      * @return {String}
36706      */
36707     getDragDropText : function(){
36708         var count = this.selModel.getCount();
36709         return String.format(this.ddText, count, count == 1 ? '' : 's');
36710     }
36711 });
36712 /**
36713  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36714  * %0 is replaced with the number of selected rows.
36715  * @type String
36716  */
36717 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36718  * Based on:
36719  * Ext JS Library 1.1.1
36720  * Copyright(c) 2006-2007, Ext JS, LLC.
36721  *
36722  * Originally Released Under LGPL - original licence link has changed is not relivant.
36723  *
36724  * Fork - LGPL
36725  * <script type="text/javascript">
36726  */
36727  
36728 Roo.grid.AbstractGridView = function(){
36729         this.grid = null;
36730         
36731         this.events = {
36732             "beforerowremoved" : true,
36733             "beforerowsinserted" : true,
36734             "beforerefresh" : true,
36735             "rowremoved" : true,
36736             "rowsinserted" : true,
36737             "rowupdated" : true,
36738             "refresh" : true
36739         };
36740     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36741 };
36742
36743 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36744     rowClass : "x-grid-row",
36745     cellClass : "x-grid-cell",
36746     tdClass : "x-grid-td",
36747     hdClass : "x-grid-hd",
36748     splitClass : "x-grid-hd-split",
36749     
36750     init: function(grid){
36751         this.grid = grid;
36752                 var cid = this.grid.getGridEl().id;
36753         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36754         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36755         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36756         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36757         },
36758         
36759     getColumnRenderers : function(){
36760         var renderers = [];
36761         var cm = this.grid.colModel;
36762         var colCount = cm.getColumnCount();
36763         for(var i = 0; i < colCount; i++){
36764             renderers[i] = cm.getRenderer(i);
36765         }
36766         return renderers;
36767     },
36768     
36769     getColumnIds : function(){
36770         var ids = [];
36771         var cm = this.grid.colModel;
36772         var colCount = cm.getColumnCount();
36773         for(var i = 0; i < colCount; i++){
36774             ids[i] = cm.getColumnId(i);
36775         }
36776         return ids;
36777     },
36778     
36779     getDataIndexes : function(){
36780         if(!this.indexMap){
36781             this.indexMap = this.buildIndexMap();
36782         }
36783         return this.indexMap.colToData;
36784     },
36785     
36786     getColumnIndexByDataIndex : function(dataIndex){
36787         if(!this.indexMap){
36788             this.indexMap = this.buildIndexMap();
36789         }
36790         return this.indexMap.dataToCol[dataIndex];
36791     },
36792     
36793     /**
36794      * Set a css style for a column dynamically. 
36795      * @param {Number} colIndex The index of the column
36796      * @param {String} name The css property name
36797      * @param {String} value The css value
36798      */
36799     setCSSStyle : function(colIndex, name, value){
36800         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36801         Roo.util.CSS.updateRule(selector, name, value);
36802     },
36803     
36804     generateRules : function(cm){
36805         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36806         Roo.util.CSS.removeStyleSheet(rulesId);
36807         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36808             var cid = cm.getColumnId(i);
36809             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36810                          this.tdSelector, cid, " {\n}\n",
36811                          this.hdSelector, cid, " {\n}\n",
36812                          this.splitSelector, cid, " {\n}\n");
36813         }
36814         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36815     }
36816 });/*
36817  * Based on:
36818  * Ext JS Library 1.1.1
36819  * Copyright(c) 2006-2007, Ext JS, LLC.
36820  *
36821  * Originally Released Under LGPL - original licence link has changed is not relivant.
36822  *
36823  * Fork - LGPL
36824  * <script type="text/javascript">
36825  */
36826
36827 // private
36828 // This is a support class used internally by the Grid components
36829 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36830     this.grid = grid;
36831     this.view = grid.getView();
36832     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36833     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36834     if(hd2){
36835         this.setHandleElId(Roo.id(hd));
36836         this.setOuterHandleElId(Roo.id(hd2));
36837     }
36838     this.scroll = false;
36839 };
36840 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36841     maxDragWidth: 120,
36842     getDragData : function(e){
36843         var t = Roo.lib.Event.getTarget(e);
36844         var h = this.view.findHeaderCell(t);
36845         if(h){
36846             return {ddel: h.firstChild, header:h};
36847         }
36848         return false;
36849     },
36850
36851     onInitDrag : function(e){
36852         this.view.headersDisabled = true;
36853         var clone = this.dragData.ddel.cloneNode(true);
36854         clone.id = Roo.id();
36855         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36856         this.proxy.update(clone);
36857         return true;
36858     },
36859
36860     afterValidDrop : function(){
36861         var v = this.view;
36862         setTimeout(function(){
36863             v.headersDisabled = false;
36864         }, 50);
36865     },
36866
36867     afterInvalidDrop : function(){
36868         var v = this.view;
36869         setTimeout(function(){
36870             v.headersDisabled = false;
36871         }, 50);
36872     }
36873 });
36874 /*
36875  * Based on:
36876  * Ext JS Library 1.1.1
36877  * Copyright(c) 2006-2007, Ext JS, LLC.
36878  *
36879  * Originally Released Under LGPL - original licence link has changed is not relivant.
36880  *
36881  * Fork - LGPL
36882  * <script type="text/javascript">
36883  */
36884 // private
36885 // This is a support class used internally by the Grid components
36886 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36887     this.grid = grid;
36888     this.view = grid.getView();
36889     // split the proxies so they don't interfere with mouse events
36890     this.proxyTop = Roo.DomHelper.append(document.body, {
36891         cls:"col-move-top", html:"&#160;"
36892     }, true);
36893     this.proxyBottom = Roo.DomHelper.append(document.body, {
36894         cls:"col-move-bottom", html:"&#160;"
36895     }, true);
36896     this.proxyTop.hide = this.proxyBottom.hide = function(){
36897         this.setLeftTop(-100,-100);
36898         this.setStyle("visibility", "hidden");
36899     };
36900     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36901     // temporarily disabled
36902     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36903     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36904 };
36905 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36906     proxyOffsets : [-4, -9],
36907     fly: Roo.Element.fly,
36908
36909     getTargetFromEvent : function(e){
36910         var t = Roo.lib.Event.getTarget(e);
36911         var cindex = this.view.findCellIndex(t);
36912         if(cindex !== false){
36913             return this.view.getHeaderCell(cindex);
36914         }
36915         return null;
36916     },
36917
36918     nextVisible : function(h){
36919         var v = this.view, cm = this.grid.colModel;
36920         h = h.nextSibling;
36921         while(h){
36922             if(!cm.isHidden(v.getCellIndex(h))){
36923                 return h;
36924             }
36925             h = h.nextSibling;
36926         }
36927         return null;
36928     },
36929
36930     prevVisible : function(h){
36931         var v = this.view, cm = this.grid.colModel;
36932         h = h.prevSibling;
36933         while(h){
36934             if(!cm.isHidden(v.getCellIndex(h))){
36935                 return h;
36936             }
36937             h = h.prevSibling;
36938         }
36939         return null;
36940     },
36941
36942     positionIndicator : function(h, n, e){
36943         var x = Roo.lib.Event.getPageX(e);
36944         var r = Roo.lib.Dom.getRegion(n.firstChild);
36945         var px, pt, py = r.top + this.proxyOffsets[1];
36946         if((r.right - x) <= (r.right-r.left)/2){
36947             px = r.right+this.view.borderWidth;
36948             pt = "after";
36949         }else{
36950             px = r.left;
36951             pt = "before";
36952         }
36953         var oldIndex = this.view.getCellIndex(h);
36954         var newIndex = this.view.getCellIndex(n);
36955
36956         if(this.grid.colModel.isFixed(newIndex)){
36957             return false;
36958         }
36959
36960         var locked = this.grid.colModel.isLocked(newIndex);
36961
36962         if(pt == "after"){
36963             newIndex++;
36964         }
36965         if(oldIndex < newIndex){
36966             newIndex--;
36967         }
36968         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36969             return false;
36970         }
36971         px +=  this.proxyOffsets[0];
36972         this.proxyTop.setLeftTop(px, py);
36973         this.proxyTop.show();
36974         if(!this.bottomOffset){
36975             this.bottomOffset = this.view.mainHd.getHeight();
36976         }
36977         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36978         this.proxyBottom.show();
36979         return pt;
36980     },
36981
36982     onNodeEnter : function(n, dd, e, data){
36983         if(data.header != n){
36984             this.positionIndicator(data.header, n, e);
36985         }
36986     },
36987
36988     onNodeOver : function(n, dd, e, data){
36989         var result = false;
36990         if(data.header != n){
36991             result = this.positionIndicator(data.header, n, e);
36992         }
36993         if(!result){
36994             this.proxyTop.hide();
36995             this.proxyBottom.hide();
36996         }
36997         return result ? this.dropAllowed : this.dropNotAllowed;
36998     },
36999
37000     onNodeOut : function(n, dd, e, data){
37001         this.proxyTop.hide();
37002         this.proxyBottom.hide();
37003     },
37004
37005     onNodeDrop : function(n, dd, e, data){
37006         var h = data.header;
37007         if(h != n){
37008             var cm = this.grid.colModel;
37009             var x = Roo.lib.Event.getPageX(e);
37010             var r = Roo.lib.Dom.getRegion(n.firstChild);
37011             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37012             var oldIndex = this.view.getCellIndex(h);
37013             var newIndex = this.view.getCellIndex(n);
37014             var locked = cm.isLocked(newIndex);
37015             if(pt == "after"){
37016                 newIndex++;
37017             }
37018             if(oldIndex < newIndex){
37019                 newIndex--;
37020             }
37021             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37022                 return false;
37023             }
37024             cm.setLocked(oldIndex, locked, true);
37025             cm.moveColumn(oldIndex, newIndex);
37026             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37027             return true;
37028         }
37029         return false;
37030     }
37031 });
37032 /*
37033  * Based on:
37034  * Ext JS Library 1.1.1
37035  * Copyright(c) 2006-2007, Ext JS, LLC.
37036  *
37037  * Originally Released Under LGPL - original licence link has changed is not relivant.
37038  *
37039  * Fork - LGPL
37040  * <script type="text/javascript">
37041  */
37042   
37043 /**
37044  * @class Roo.grid.GridView
37045  * @extends Roo.util.Observable
37046  *
37047  * @constructor
37048  * @param {Object} config
37049  */
37050 Roo.grid.GridView = function(config){
37051     Roo.grid.GridView.superclass.constructor.call(this);
37052     this.el = null;
37053
37054     Roo.apply(this, config);
37055 };
37056
37057 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37058
37059     unselectable :  'unselectable="on"',
37060     unselectableCls :  'x-unselectable',
37061     
37062     
37063     rowClass : "x-grid-row",
37064
37065     cellClass : "x-grid-col",
37066
37067     tdClass : "x-grid-td",
37068
37069     hdClass : "x-grid-hd",
37070
37071     splitClass : "x-grid-split",
37072
37073     sortClasses : ["sort-asc", "sort-desc"],
37074
37075     enableMoveAnim : false,
37076
37077     hlColor: "C3DAF9",
37078
37079     dh : Roo.DomHelper,
37080
37081     fly : Roo.Element.fly,
37082
37083     css : Roo.util.CSS,
37084
37085     borderWidth: 1,
37086
37087     splitOffset: 3,
37088
37089     scrollIncrement : 22,
37090
37091     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37092
37093     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37094
37095     bind : function(ds, cm){
37096         if(this.ds){
37097             this.ds.un("load", this.onLoad, this);
37098             this.ds.un("datachanged", this.onDataChange, this);
37099             this.ds.un("add", this.onAdd, this);
37100             this.ds.un("remove", this.onRemove, this);
37101             this.ds.un("update", this.onUpdate, this);
37102             this.ds.un("clear", this.onClear, this);
37103         }
37104         if(ds){
37105             ds.on("load", this.onLoad, this);
37106             ds.on("datachanged", this.onDataChange, this);
37107             ds.on("add", this.onAdd, this);
37108             ds.on("remove", this.onRemove, this);
37109             ds.on("update", this.onUpdate, this);
37110             ds.on("clear", this.onClear, this);
37111         }
37112         this.ds = ds;
37113
37114         if(this.cm){
37115             this.cm.un("widthchange", this.onColWidthChange, this);
37116             this.cm.un("headerchange", this.onHeaderChange, this);
37117             this.cm.un("hiddenchange", this.onHiddenChange, this);
37118             this.cm.un("columnmoved", this.onColumnMove, this);
37119             this.cm.un("columnlockchange", this.onColumnLock, this);
37120         }
37121         if(cm){
37122             this.generateRules(cm);
37123             cm.on("widthchange", this.onColWidthChange, this);
37124             cm.on("headerchange", this.onHeaderChange, this);
37125             cm.on("hiddenchange", this.onHiddenChange, this);
37126             cm.on("columnmoved", this.onColumnMove, this);
37127             cm.on("columnlockchange", this.onColumnLock, this);
37128         }
37129         this.cm = cm;
37130     },
37131
37132     init: function(grid){
37133         Roo.grid.GridView.superclass.init.call(this, grid);
37134
37135         this.bind(grid.dataSource, grid.colModel);
37136
37137         grid.on("headerclick", this.handleHeaderClick, this);
37138
37139         if(grid.trackMouseOver){
37140             grid.on("mouseover", this.onRowOver, this);
37141             grid.on("mouseout", this.onRowOut, this);
37142         }
37143         grid.cancelTextSelection = function(){};
37144         this.gridId = grid.id;
37145
37146         var tpls = this.templates || {};
37147
37148         if(!tpls.master){
37149             tpls.master = new Roo.Template(
37150                '<div class="x-grid" hidefocus="true">',
37151                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37152                   '<div class="x-grid-topbar"></div>',
37153                   '<div class="x-grid-scroller"><div></div></div>',
37154                   '<div class="x-grid-locked">',
37155                       '<div class="x-grid-header">{lockedHeader}</div>',
37156                       '<div class="x-grid-body">{lockedBody}</div>',
37157                   "</div>",
37158                   '<div class="x-grid-viewport">',
37159                       '<div class="x-grid-header">{header}</div>',
37160                       '<div class="x-grid-body">{body}</div>',
37161                   "</div>",
37162                   '<div class="x-grid-bottombar"></div>',
37163                  
37164                   '<div class="x-grid-resize-proxy">&#160;</div>',
37165                "</div>"
37166             );
37167             tpls.master.disableformats = true;
37168         }
37169
37170         if(!tpls.header){
37171             tpls.header = new Roo.Template(
37172                '<table border="0" cellspacing="0" cellpadding="0">',
37173                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37174                "</table>{splits}"
37175             );
37176             tpls.header.disableformats = true;
37177         }
37178         tpls.header.compile();
37179
37180         if(!tpls.hcell){
37181             tpls.hcell = new Roo.Template(
37182                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div " title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37183                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37184                 "</div></td>"
37185              );
37186              tpls.hcell.disableFormats = true;
37187         }
37188         tpls.hcell.compile();
37189
37190         if(!tpls.hsplit){
37191             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37192                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37193             tpls.hsplit.disableFormats = true;
37194         }
37195         tpls.hsplit.compile();
37196
37197         if(!tpls.body){
37198             tpls.body = new Roo.Template(
37199                '<table border="0" cellspacing="0" cellpadding="0">',
37200                "<tbody>{rows}</tbody>",
37201                "</table>"
37202             );
37203             tpls.body.disableFormats = true;
37204         }
37205         tpls.body.compile();
37206
37207         if(!tpls.row){
37208             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37209             tpls.row.disableFormats = true;
37210         }
37211         tpls.row.compile();
37212
37213         if(!tpls.cell){
37214             tpls.cell = new Roo.Template(
37215                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37216                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37217                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37218                 "</td>"
37219             );
37220             tpls.cell.disableFormats = true;
37221         }
37222         tpls.cell.compile();
37223
37224         this.templates = tpls;
37225     },
37226
37227     // remap these for backwards compat
37228     onColWidthChange : function(){
37229         this.updateColumns.apply(this, arguments);
37230     },
37231     onHeaderChange : function(){
37232         this.updateHeaders.apply(this, arguments);
37233     }, 
37234     onHiddenChange : function(){
37235         this.handleHiddenChange.apply(this, arguments);
37236     },
37237     onColumnMove : function(){
37238         this.handleColumnMove.apply(this, arguments);
37239     },
37240     onColumnLock : function(){
37241         this.handleLockChange.apply(this, arguments);
37242     },
37243
37244     onDataChange : function(){
37245         this.refresh();
37246         this.updateHeaderSortState();
37247     },
37248
37249     onClear : function(){
37250         this.refresh();
37251     },
37252
37253     onUpdate : function(ds, record){
37254         this.refreshRow(record);
37255     },
37256
37257     refreshRow : function(record){
37258         var ds = this.ds, index;
37259         if(typeof record == 'number'){
37260             index = record;
37261             record = ds.getAt(index);
37262         }else{
37263             index = ds.indexOf(record);
37264         }
37265         this.insertRows(ds, index, index, true);
37266         this.onRemove(ds, record, index+1, true);
37267         this.syncRowHeights(index, index);
37268         this.layout();
37269         this.fireEvent("rowupdated", this, index, record);
37270     },
37271
37272     onAdd : function(ds, records, index){
37273         this.insertRows(ds, index, index + (records.length-1));
37274     },
37275
37276     onRemove : function(ds, record, index, isUpdate){
37277         if(isUpdate !== true){
37278             this.fireEvent("beforerowremoved", this, index, record);
37279         }
37280         var bt = this.getBodyTable(), lt = this.getLockedTable();
37281         if(bt.rows[index]){
37282             bt.firstChild.removeChild(bt.rows[index]);
37283         }
37284         if(lt.rows[index]){
37285             lt.firstChild.removeChild(lt.rows[index]);
37286         }
37287         if(isUpdate !== true){
37288             this.stripeRows(index);
37289             this.syncRowHeights(index, index);
37290             this.layout();
37291             this.fireEvent("rowremoved", this, index, record);
37292         }
37293     },
37294
37295     onLoad : function(){
37296         this.scrollToTop();
37297     },
37298
37299     /**
37300      * Scrolls the grid to the top
37301      */
37302     scrollToTop : function(){
37303         if(this.scroller){
37304             this.scroller.dom.scrollTop = 0;
37305             this.syncScroll();
37306         }
37307     },
37308
37309     /**
37310      * Gets a panel in the header of the grid that can be used for toolbars etc.
37311      * After modifying the contents of this panel a call to grid.autoSize() may be
37312      * required to register any changes in size.
37313      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37314      * @return Roo.Element
37315      */
37316     getHeaderPanel : function(doShow){
37317         if(doShow){
37318             this.headerPanel.show();
37319         }
37320         return this.headerPanel;
37321     },
37322
37323     /**
37324      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37325      * After modifying the contents of this panel a call to grid.autoSize() may be
37326      * required to register any changes in size.
37327      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37328      * @return Roo.Element
37329      */
37330     getFooterPanel : function(doShow){
37331         if(doShow){
37332             this.footerPanel.show();
37333         }
37334         return this.footerPanel;
37335     },
37336
37337     initElements : function(){
37338         var E = Roo.Element;
37339         var el = this.grid.getGridEl().dom.firstChild;
37340         var cs = el.childNodes;
37341
37342         this.el = new E(el);
37343         
37344          this.focusEl = new E(el.firstChild);
37345         this.focusEl.swallowEvent("click", true);
37346         
37347         this.headerPanel = new E(cs[1]);
37348         this.headerPanel.enableDisplayMode("block");
37349
37350         this.scroller = new E(cs[2]);
37351         this.scrollSizer = new E(this.scroller.dom.firstChild);
37352
37353         this.lockedWrap = new E(cs[3]);
37354         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37355         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37356
37357         this.mainWrap = new E(cs[4]);
37358         this.mainHd = new E(this.mainWrap.dom.firstChild);
37359         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37360
37361         this.footerPanel = new E(cs[5]);
37362         this.footerPanel.enableDisplayMode("block");
37363
37364         this.resizeProxy = new E(cs[6]);
37365
37366         this.headerSelector = String.format(
37367            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37368            this.lockedHd.id, this.mainHd.id
37369         );
37370
37371         this.splitterSelector = String.format(
37372            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37373            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37374         );
37375     },
37376     idToCssName : function(s)
37377     {
37378         return s.replace(/[^a-z0-9]+/ig, '-');
37379     },
37380
37381     getHeaderCell : function(index){
37382         return Roo.DomQuery.select(this.headerSelector)[index];
37383     },
37384
37385     getHeaderCellMeasure : function(index){
37386         return this.getHeaderCell(index).firstChild;
37387     },
37388
37389     getHeaderCellText : function(index){
37390         return this.getHeaderCell(index).firstChild.firstChild;
37391     },
37392
37393     getLockedTable : function(){
37394         return this.lockedBody.dom.firstChild;
37395     },
37396
37397     getBodyTable : function(){
37398         return this.mainBody.dom.firstChild;
37399     },
37400
37401     getLockedRow : function(index){
37402         return this.getLockedTable().rows[index];
37403     },
37404
37405     getRow : function(index){
37406         return this.getBodyTable().rows[index];
37407     },
37408
37409     getRowComposite : function(index){
37410         if(!this.rowEl){
37411             this.rowEl = new Roo.CompositeElementLite();
37412         }
37413         var els = [], lrow, mrow;
37414         if(lrow = this.getLockedRow(index)){
37415             els.push(lrow);
37416         }
37417         if(mrow = this.getRow(index)){
37418             els.push(mrow);
37419         }
37420         this.rowEl.elements = els;
37421         return this.rowEl;
37422     },
37423     /**
37424      * Gets the 'td' of the cell
37425      * 
37426      * @param {Integer} rowIndex row to select
37427      * @param {Integer} colIndex column to select
37428      * 
37429      * @return {Object} 
37430      */
37431     getCell : function(rowIndex, colIndex){
37432         var locked = this.cm.getLockedCount();
37433         var source;
37434         if(colIndex < locked){
37435             source = this.lockedBody.dom.firstChild;
37436         }else{
37437             source = this.mainBody.dom.firstChild;
37438             colIndex -= locked;
37439         }
37440         return source.rows[rowIndex].childNodes[colIndex];
37441     },
37442
37443     getCellText : function(rowIndex, colIndex){
37444         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37445     },
37446
37447     getCellBox : function(cell){
37448         var b = this.fly(cell).getBox();
37449         if(Roo.isOpera){ // opera fails to report the Y
37450             b.y = cell.offsetTop + this.mainBody.getY();
37451         }
37452         return b;
37453     },
37454
37455     getCellIndex : function(cell){
37456         var id = String(cell.className).match(this.cellRE);
37457         if(id){
37458             return parseInt(id[1], 10);
37459         }
37460         return 0;
37461     },
37462
37463     findHeaderIndex : function(n){
37464         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37465         return r ? this.getCellIndex(r) : false;
37466     },
37467
37468     findHeaderCell : function(n){
37469         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37470         return r ? r : false;
37471     },
37472
37473     findRowIndex : function(n){
37474         if(!n){
37475             return false;
37476         }
37477         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37478         return r ? r.rowIndex : false;
37479     },
37480
37481     findCellIndex : function(node){
37482         var stop = this.el.dom;
37483         while(node && node != stop){
37484             if(this.findRE.test(node.className)){
37485                 return this.getCellIndex(node);
37486             }
37487             node = node.parentNode;
37488         }
37489         return false;
37490     },
37491
37492     getColumnId : function(index){
37493         return this.cm.getColumnId(index);
37494     },
37495
37496     getSplitters : function()
37497     {
37498         if(this.splitterSelector){
37499            return Roo.DomQuery.select(this.splitterSelector);
37500         }else{
37501             return null;
37502       }
37503     },
37504
37505     getSplitter : function(index){
37506         return this.getSplitters()[index];
37507     },
37508
37509     onRowOver : function(e, t){
37510         var row;
37511         if((row = this.findRowIndex(t)) !== false){
37512             this.getRowComposite(row).addClass("x-grid-row-over");
37513         }
37514     },
37515
37516     onRowOut : function(e, t){
37517         var row;
37518         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37519             this.getRowComposite(row).removeClass("x-grid-row-over");
37520         }
37521     },
37522
37523     renderHeaders : function(){
37524         var cm = this.cm;
37525         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37526         var cb = [], lb = [], sb = [], lsb = [], p = {};
37527         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37528             p.cellId = "x-grid-hd-0-" + i;
37529             p.splitId = "x-grid-csplit-0-" + i;
37530             p.id = cm.getColumnId(i);
37531             p.title = cm.getColumnTooltip(i) || cm.getColumnHeader(i) || "";
37532             p.value = cm.getColumnHeader(i) || "";
37533             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37534             if(!cm.isLocked(i)){
37535                 cb[cb.length] = ct.apply(p);
37536                 sb[sb.length] = st.apply(p);
37537             }else{
37538                 lb[lb.length] = ct.apply(p);
37539                 lsb[lsb.length] = st.apply(p);
37540             }
37541         }
37542         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37543                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37544     },
37545
37546     updateHeaders : function(){
37547         var html = this.renderHeaders();
37548         this.lockedHd.update(html[0]);
37549         this.mainHd.update(html[1]);
37550     },
37551
37552     /**
37553      * Focuses the specified row.
37554      * @param {Number} row The row index
37555      */
37556     focusRow : function(row)
37557     {
37558         //Roo.log('GridView.focusRow');
37559         var x = this.scroller.dom.scrollLeft;
37560         this.focusCell(row, 0, false);
37561         this.scroller.dom.scrollLeft = x;
37562     },
37563
37564     /**
37565      * Focuses the specified cell.
37566      * @param {Number} row The row index
37567      * @param {Number} col The column index
37568      * @param {Boolean} hscroll false to disable horizontal scrolling
37569      */
37570     focusCell : function(row, col, hscroll)
37571     {
37572         //Roo.log('GridView.focusCell');
37573         var el = this.ensureVisible(row, col, hscroll);
37574         this.focusEl.alignTo(el, "tl-tl");
37575         if(Roo.isGecko){
37576             this.focusEl.focus();
37577         }else{
37578             this.focusEl.focus.defer(1, this.focusEl);
37579         }
37580     },
37581
37582     /**
37583      * Scrolls the specified cell into view
37584      * @param {Number} row The row index
37585      * @param {Number} col The column index
37586      * @param {Boolean} hscroll false to disable horizontal scrolling
37587      */
37588     ensureVisible : function(row, col, hscroll)
37589     {
37590         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37591         //return null; //disable for testing.
37592         if(typeof row != "number"){
37593             row = row.rowIndex;
37594         }
37595         if(row < 0 && row >= this.ds.getCount()){
37596             return  null;
37597         }
37598         col = (col !== undefined ? col : 0);
37599         var cm = this.grid.colModel;
37600         while(cm.isHidden(col)){
37601             col++;
37602         }
37603
37604         var el = this.getCell(row, col);
37605         if(!el){
37606             return null;
37607         }
37608         var c = this.scroller.dom;
37609
37610         var ctop = parseInt(el.offsetTop, 10);
37611         var cleft = parseInt(el.offsetLeft, 10);
37612         var cbot = ctop + el.offsetHeight;
37613         var cright = cleft + el.offsetWidth;
37614         
37615         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37616         var stop = parseInt(c.scrollTop, 10);
37617         var sleft = parseInt(c.scrollLeft, 10);
37618         var sbot = stop + ch;
37619         var sright = sleft + c.clientWidth;
37620         /*
37621         Roo.log('GridView.ensureVisible:' +
37622                 ' ctop:' + ctop +
37623                 ' c.clientHeight:' + c.clientHeight +
37624                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37625                 ' stop:' + stop +
37626                 ' cbot:' + cbot +
37627                 ' sbot:' + sbot +
37628                 ' ch:' + ch  
37629                 );
37630         */
37631         if(ctop < stop){
37632              c.scrollTop = ctop;
37633             //Roo.log("set scrolltop to ctop DISABLE?");
37634         }else if(cbot > sbot){
37635             //Roo.log("set scrolltop to cbot-ch");
37636             c.scrollTop = cbot-ch;
37637         }
37638         
37639         if(hscroll !== false){
37640             if(cleft < sleft){
37641                 c.scrollLeft = cleft;
37642             }else if(cright > sright){
37643                 c.scrollLeft = cright-c.clientWidth;
37644             }
37645         }
37646          
37647         return el;
37648     },
37649
37650     updateColumns : function(){
37651         this.grid.stopEditing();
37652         var cm = this.grid.colModel, colIds = this.getColumnIds();
37653         //var totalWidth = cm.getTotalWidth();
37654         var pos = 0;
37655         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37656             //if(cm.isHidden(i)) continue;
37657             var w = cm.getColumnWidth(i);
37658             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37659             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37660         }
37661         this.updateSplitters();
37662     },
37663
37664     generateRules : function(cm){
37665         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37666         Roo.util.CSS.removeStyleSheet(rulesId);
37667         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37668             var cid = cm.getColumnId(i);
37669             var align = '';
37670             if(cm.config[i].align){
37671                 align = 'text-align:'+cm.config[i].align+';';
37672             }
37673             var hidden = '';
37674             if(cm.isHidden(i)){
37675                 hidden = 'display:none;';
37676             }
37677             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37678             ruleBuf.push(
37679                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37680                     this.hdSelector, cid, " {\n", align, width, "}\n",
37681                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37682                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37683         }
37684         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37685     },
37686
37687     updateSplitters : function(){
37688         var cm = this.cm, s = this.getSplitters();
37689         if(s){ // splitters not created yet
37690             var pos = 0, locked = true;
37691             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37692                 if(cm.isHidden(i)) {
37693                     continue;
37694                 }
37695                 var w = cm.getColumnWidth(i); // make sure it's a number
37696                 if(!cm.isLocked(i) && locked){
37697                     pos = 0;
37698                     locked = false;
37699                 }
37700                 pos += w;
37701                 s[i].style.left = (pos-this.splitOffset) + "px";
37702             }
37703         }
37704     },
37705
37706     handleHiddenChange : function(colModel, colIndex, hidden){
37707         if(hidden){
37708             this.hideColumn(colIndex);
37709         }else{
37710             this.unhideColumn(colIndex);
37711         }
37712     },
37713
37714     hideColumn : function(colIndex){
37715         var cid = this.getColumnId(colIndex);
37716         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37717         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37718         if(Roo.isSafari){
37719             this.updateHeaders();
37720         }
37721         this.updateSplitters();
37722         this.layout();
37723     },
37724
37725     unhideColumn : function(colIndex){
37726         var cid = this.getColumnId(colIndex);
37727         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37728         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37729
37730         if(Roo.isSafari){
37731             this.updateHeaders();
37732         }
37733         this.updateSplitters();
37734         this.layout();
37735     },
37736
37737     insertRows : function(dm, firstRow, lastRow, isUpdate){
37738         if(firstRow == 0 && lastRow == dm.getCount()-1){
37739             this.refresh();
37740         }else{
37741             if(!isUpdate){
37742                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37743             }
37744             var s = this.getScrollState();
37745             var markup = this.renderRows(firstRow, lastRow);
37746             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37747             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37748             this.restoreScroll(s);
37749             if(!isUpdate){
37750                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37751                 this.syncRowHeights(firstRow, lastRow);
37752                 this.stripeRows(firstRow);
37753                 this.layout();
37754             }
37755         }
37756     },
37757
37758     bufferRows : function(markup, target, index){
37759         var before = null, trows = target.rows, tbody = target.tBodies[0];
37760         if(index < trows.length){
37761             before = trows[index];
37762         }
37763         var b = document.createElement("div");
37764         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37765         var rows = b.firstChild.rows;
37766         for(var i = 0, len = rows.length; i < len; i++){
37767             if(before){
37768                 tbody.insertBefore(rows[0], before);
37769             }else{
37770                 tbody.appendChild(rows[0]);
37771             }
37772         }
37773         b.innerHTML = "";
37774         b = null;
37775     },
37776
37777     deleteRows : function(dm, firstRow, lastRow){
37778         if(dm.getRowCount()<1){
37779             this.fireEvent("beforerefresh", this);
37780             this.mainBody.update("");
37781             this.lockedBody.update("");
37782             this.fireEvent("refresh", this);
37783         }else{
37784             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37785             var bt = this.getBodyTable();
37786             var tbody = bt.firstChild;
37787             var rows = bt.rows;
37788             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37789                 tbody.removeChild(rows[firstRow]);
37790             }
37791             this.stripeRows(firstRow);
37792             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37793         }
37794     },
37795
37796     updateRows : function(dataSource, firstRow, lastRow){
37797         var s = this.getScrollState();
37798         this.refresh();
37799         this.restoreScroll(s);
37800     },
37801
37802     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37803         if(!noRefresh){
37804            this.refresh();
37805         }
37806         this.updateHeaderSortState();
37807     },
37808
37809     getScrollState : function(){
37810         
37811         var sb = this.scroller.dom;
37812         return {left: sb.scrollLeft, top: sb.scrollTop};
37813     },
37814
37815     stripeRows : function(startRow){
37816         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37817             return;
37818         }
37819         startRow = startRow || 0;
37820         var rows = this.getBodyTable().rows;
37821         var lrows = this.getLockedTable().rows;
37822         var cls = ' x-grid-row-alt ';
37823         for(var i = startRow, len = rows.length; i < len; i++){
37824             var row = rows[i], lrow = lrows[i];
37825             var isAlt = ((i+1) % 2 == 0);
37826             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37827             if(isAlt == hasAlt){
37828                 continue;
37829             }
37830             if(isAlt){
37831                 row.className += " x-grid-row-alt";
37832             }else{
37833                 row.className = row.className.replace("x-grid-row-alt", "");
37834             }
37835             if(lrow){
37836                 lrow.className = row.className;
37837             }
37838         }
37839     },
37840
37841     restoreScroll : function(state){
37842         //Roo.log('GridView.restoreScroll');
37843         var sb = this.scroller.dom;
37844         sb.scrollLeft = state.left;
37845         sb.scrollTop = state.top;
37846         this.syncScroll();
37847     },
37848
37849     syncScroll : function(){
37850         //Roo.log('GridView.syncScroll');
37851         var sb = this.scroller.dom;
37852         var sh = this.mainHd.dom;
37853         var bs = this.mainBody.dom;
37854         var lv = this.lockedBody.dom;
37855         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37856         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37857     },
37858
37859     handleScroll : function(e){
37860         this.syncScroll();
37861         var sb = this.scroller.dom;
37862         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37863         e.stopEvent();
37864     },
37865
37866     handleWheel : function(e){
37867         var d = e.getWheelDelta();
37868         this.scroller.dom.scrollTop -= d*22;
37869         // set this here to prevent jumpy scrolling on large tables
37870         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37871         e.stopEvent();
37872     },
37873
37874     renderRows : function(startRow, endRow){
37875         // pull in all the crap needed to render rows
37876         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37877         var colCount = cm.getColumnCount();
37878
37879         if(ds.getCount() < 1){
37880             return ["", ""];
37881         }
37882
37883         // build a map for all the columns
37884         var cs = [];
37885         for(var i = 0; i < colCount; i++){
37886             var name = cm.getDataIndex(i);
37887             cs[i] = {
37888                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37889                 renderer : cm.getRenderer(i),
37890                 id : cm.getColumnId(i),
37891                 locked : cm.isLocked(i),
37892                 has_editor : cm.isCellEditable(i)
37893             };
37894         }
37895
37896         startRow = startRow || 0;
37897         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37898
37899         // records to render
37900         var rs = ds.getRange(startRow, endRow);
37901
37902         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37903     },
37904
37905     // As much as I hate to duplicate code, this was branched because FireFox really hates
37906     // [].join("") on strings. The performance difference was substantial enough to
37907     // branch this function
37908     doRender : Roo.isGecko ?
37909             function(cs, rs, ds, startRow, colCount, stripe){
37910                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37911                 // buffers
37912                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37913                 
37914                 var hasListener = this.grid.hasListener('rowclass');
37915                 var rowcfg = {};
37916                 for(var j = 0, len = rs.length; j < len; j++){
37917                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37918                     for(var i = 0; i < colCount; i++){
37919                         c = cs[i];
37920                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37921                         p.id = c.id;
37922                         p.css = p.attr = "";
37923                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37924                         if(p.value == undefined || p.value === "") {
37925                             p.value = "&#160;";
37926                         }
37927                         if(c.has_editor){
37928                             p.css += ' x-grid-editable-cell';
37929                         }
37930                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37931                             p.css +=  ' x-grid-dirty-cell';
37932                         }
37933                         var markup = ct.apply(p);
37934                         if(!c.locked){
37935                             cb+= markup;
37936                         }else{
37937                             lcb+= markup;
37938                         }
37939                     }
37940                     var alt = [];
37941                     if(stripe && ((rowIndex+1) % 2 == 0)){
37942                         alt.push("x-grid-row-alt")
37943                     }
37944                     if(r.dirty){
37945                         alt.push(  " x-grid-dirty-row");
37946                     }
37947                     rp.cells = lcb;
37948                     if(this.getRowClass){
37949                         alt.push(this.getRowClass(r, rowIndex));
37950                     }
37951                     if (hasListener) {
37952                         rowcfg = {
37953                              
37954                             record: r,
37955                             rowIndex : rowIndex,
37956                             rowClass : ''
37957                         };
37958                         this.grid.fireEvent('rowclass', this, rowcfg);
37959                         alt.push(rowcfg.rowClass);
37960                     }
37961                     rp.alt = alt.join(" ");
37962                     lbuf+= rt.apply(rp);
37963                     rp.cells = cb;
37964                     buf+=  rt.apply(rp);
37965                 }
37966                 return [lbuf, buf];
37967             } :
37968             function(cs, rs, ds, startRow, colCount, stripe){
37969                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37970                 // buffers
37971                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37972                 var hasListener = this.grid.hasListener('rowclass');
37973  
37974                 var rowcfg = {};
37975                 for(var j = 0, len = rs.length; j < len; j++){
37976                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37977                     for(var i = 0; i < colCount; i++){
37978                         c = cs[i];
37979                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37980                         p.id = c.id;
37981                         p.css = p.attr = "";
37982                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37983                         if(p.value == undefined || p.value === "") {
37984                             p.value = "&#160;";
37985                         }
37986                         //Roo.log(c);
37987                          if(c.has_editor){
37988                             p.css += ' x-grid-editable-cell';
37989                         }
37990                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37991                             p.css += ' x-grid-dirty-cell' 
37992                         }
37993                         
37994                         var markup = ct.apply(p);
37995                         if(!c.locked){
37996                             cb[cb.length] = markup;
37997                         }else{
37998                             lcb[lcb.length] = markup;
37999                         }
38000                     }
38001                     var alt = [];
38002                     if(stripe && ((rowIndex+1) % 2 == 0)){
38003                         alt.push( "x-grid-row-alt");
38004                     }
38005                     if(r.dirty){
38006                         alt.push(" x-grid-dirty-row");
38007                     }
38008                     rp.cells = lcb;
38009                     if(this.getRowClass){
38010                         alt.push( this.getRowClass(r, rowIndex));
38011                     }
38012                     if (hasListener) {
38013                         rowcfg = {
38014                              
38015                             record: r,
38016                             rowIndex : rowIndex,
38017                             rowClass : ''
38018                         };
38019                         this.grid.fireEvent('rowclass', this, rowcfg);
38020                         alt.push(rowcfg.rowClass);
38021                     }
38022                     
38023                     rp.alt = alt.join(" ");
38024                     rp.cells = lcb.join("");
38025                     lbuf[lbuf.length] = rt.apply(rp);
38026                     rp.cells = cb.join("");
38027                     buf[buf.length] =  rt.apply(rp);
38028                 }
38029                 return [lbuf.join(""), buf.join("")];
38030             },
38031
38032     renderBody : function(){
38033         var markup = this.renderRows();
38034         var bt = this.templates.body;
38035         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38036     },
38037
38038     /**
38039      * Refreshes the grid
38040      * @param {Boolean} headersToo
38041      */
38042     refresh : function(headersToo){
38043         this.fireEvent("beforerefresh", this);
38044         this.grid.stopEditing();
38045         var result = this.renderBody();
38046         this.lockedBody.update(result[0]);
38047         this.mainBody.update(result[1]);
38048         if(headersToo === true){
38049             this.updateHeaders();
38050             this.updateColumns();
38051             this.updateSplitters();
38052             this.updateHeaderSortState();
38053         }
38054         this.syncRowHeights();
38055         this.layout();
38056         this.fireEvent("refresh", this);
38057     },
38058
38059     handleColumnMove : function(cm, oldIndex, newIndex){
38060         this.indexMap = null;
38061         var s = this.getScrollState();
38062         this.refresh(true);
38063         this.restoreScroll(s);
38064         this.afterMove(newIndex);
38065     },
38066
38067     afterMove : function(colIndex){
38068         if(this.enableMoveAnim && Roo.enableFx){
38069             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38070         }
38071         // if multisort - fix sortOrder, and reload..
38072         if (this.grid.dataSource.multiSort) {
38073             // the we can call sort again..
38074             var dm = this.grid.dataSource;
38075             var cm = this.grid.colModel;
38076             var so = [];
38077             for(var i = 0; i < cm.config.length; i++ ) {
38078                 
38079                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38080                     continue; // dont' bother, it's not in sort list or being set.
38081                 }
38082                 
38083                 so.push(cm.config[i].dataIndex);
38084             };
38085             dm.sortOrder = so;
38086             dm.load(dm.lastOptions);
38087             
38088             
38089         }
38090         
38091     },
38092
38093     updateCell : function(dm, rowIndex, dataIndex){
38094         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38095         if(typeof colIndex == "undefined"){ // not present in grid
38096             return;
38097         }
38098         var cm = this.grid.colModel;
38099         var cell = this.getCell(rowIndex, colIndex);
38100         var cellText = this.getCellText(rowIndex, colIndex);
38101
38102         var p = {
38103             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38104             id : cm.getColumnId(colIndex),
38105             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38106         };
38107         var renderer = cm.getRenderer(colIndex);
38108         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38109         if(typeof val == "undefined" || val === "") {
38110             val = "&#160;";
38111         }
38112         cellText.innerHTML = val;
38113         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38114         this.syncRowHeights(rowIndex, rowIndex);
38115     },
38116
38117     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38118         var maxWidth = 0;
38119         if(this.grid.autoSizeHeaders){
38120             var h = this.getHeaderCellMeasure(colIndex);
38121             maxWidth = Math.max(maxWidth, h.scrollWidth);
38122         }
38123         var tb, index;
38124         if(this.cm.isLocked(colIndex)){
38125             tb = this.getLockedTable();
38126             index = colIndex;
38127         }else{
38128             tb = this.getBodyTable();
38129             index = colIndex - this.cm.getLockedCount();
38130         }
38131         if(tb && tb.rows){
38132             var rows = tb.rows;
38133             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38134             for(var i = 0; i < stopIndex; i++){
38135                 var cell = rows[i].childNodes[index].firstChild;
38136                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38137             }
38138         }
38139         return maxWidth + /*margin for error in IE*/ 5;
38140     },
38141     /**
38142      * Autofit a column to its content.
38143      * @param {Number} colIndex
38144      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38145      */
38146      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38147          if(this.cm.isHidden(colIndex)){
38148              return; // can't calc a hidden column
38149          }
38150         if(forceMinSize){
38151             var cid = this.cm.getColumnId(colIndex);
38152             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38153            if(this.grid.autoSizeHeaders){
38154                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38155            }
38156         }
38157         var newWidth = this.calcColumnWidth(colIndex);
38158         this.cm.setColumnWidth(colIndex,
38159             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38160         if(!suppressEvent){
38161             this.grid.fireEvent("columnresize", colIndex, newWidth);
38162         }
38163     },
38164
38165     /**
38166      * Autofits all columns to their content and then expands to fit any extra space in the grid
38167      */
38168      autoSizeColumns : function(){
38169         var cm = this.grid.colModel;
38170         var colCount = cm.getColumnCount();
38171         for(var i = 0; i < colCount; i++){
38172             this.autoSizeColumn(i, true, true);
38173         }
38174         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38175             this.fitColumns();
38176         }else{
38177             this.updateColumns();
38178             this.layout();
38179         }
38180     },
38181
38182     /**
38183      * Autofits all columns to the grid's width proportionate with their current size
38184      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38185      */
38186     fitColumns : function(reserveScrollSpace){
38187         var cm = this.grid.colModel;
38188         var colCount = cm.getColumnCount();
38189         var cols = [];
38190         var width = 0;
38191         var i, w;
38192         for (i = 0; i < colCount; i++){
38193             if(!cm.isHidden(i) && !cm.isFixed(i)){
38194                 w = cm.getColumnWidth(i);
38195                 cols.push(i);
38196                 cols.push(w);
38197                 width += w;
38198             }
38199         }
38200         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38201         if(reserveScrollSpace){
38202             avail -= 17;
38203         }
38204         var frac = (avail - cm.getTotalWidth())/width;
38205         while (cols.length){
38206             w = cols.pop();
38207             i = cols.pop();
38208             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38209         }
38210         this.updateColumns();
38211         this.layout();
38212     },
38213
38214     onRowSelect : function(rowIndex){
38215         var row = this.getRowComposite(rowIndex);
38216         row.addClass("x-grid-row-selected");
38217     },
38218
38219     onRowDeselect : function(rowIndex){
38220         var row = this.getRowComposite(rowIndex);
38221         row.removeClass("x-grid-row-selected");
38222     },
38223
38224     onCellSelect : function(row, col){
38225         var cell = this.getCell(row, col);
38226         if(cell){
38227             Roo.fly(cell).addClass("x-grid-cell-selected");
38228         }
38229     },
38230
38231     onCellDeselect : function(row, col){
38232         var cell = this.getCell(row, col);
38233         if(cell){
38234             Roo.fly(cell).removeClass("x-grid-cell-selected");
38235         }
38236     },
38237
38238     updateHeaderSortState : function(){
38239         
38240         // sort state can be single { field: xxx, direction : yyy}
38241         // or   { xxx=>ASC , yyy : DESC ..... }
38242         
38243         var mstate = {};
38244         if (!this.ds.multiSort) { 
38245             var state = this.ds.getSortState();
38246             if(!state){
38247                 return;
38248             }
38249             mstate[state.field] = state.direction;
38250             // FIXME... - this is not used here.. but might be elsewhere..
38251             this.sortState = state;
38252             
38253         } else {
38254             mstate = this.ds.sortToggle;
38255         }
38256         //remove existing sort classes..
38257         
38258         var sc = this.sortClasses;
38259         var hds = this.el.select(this.headerSelector).removeClass(sc);
38260         
38261         for(var f in mstate) {
38262         
38263             var sortColumn = this.cm.findColumnIndex(f);
38264             
38265             if(sortColumn != -1){
38266                 var sortDir = mstate[f];        
38267                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38268             }
38269         }
38270         
38271          
38272         
38273     },
38274
38275
38276     handleHeaderClick : function(g, index,e){
38277         
38278         Roo.log("header click");
38279         
38280         if (Roo.isTouch) {
38281             // touch events on header are handled by context
38282             this.handleHdCtx(g,index,e);
38283             return;
38284         }
38285         
38286         
38287         if(this.headersDisabled){
38288             return;
38289         }
38290         var dm = g.dataSource, cm = g.colModel;
38291         if(!cm.isSortable(index)){
38292             return;
38293         }
38294         g.stopEditing();
38295         
38296         if (dm.multiSort) {
38297             // update the sortOrder
38298             var so = [];
38299             for(var i = 0; i < cm.config.length; i++ ) {
38300                 
38301                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38302                     continue; // dont' bother, it's not in sort list or being set.
38303                 }
38304                 
38305                 so.push(cm.config[i].dataIndex);
38306             };
38307             dm.sortOrder = so;
38308         }
38309         
38310         
38311         dm.sort(cm.getDataIndex(index));
38312     },
38313
38314
38315     destroy : function(){
38316         if(this.colMenu){
38317             this.colMenu.removeAll();
38318             Roo.menu.MenuMgr.unregister(this.colMenu);
38319             this.colMenu.getEl().remove();
38320             delete this.colMenu;
38321         }
38322         if(this.hmenu){
38323             this.hmenu.removeAll();
38324             Roo.menu.MenuMgr.unregister(this.hmenu);
38325             this.hmenu.getEl().remove();
38326             delete this.hmenu;
38327         }
38328         if(this.grid.enableColumnMove){
38329             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38330             if(dds){
38331                 for(var dd in dds){
38332                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38333                         var elid = dds[dd].dragElId;
38334                         dds[dd].unreg();
38335                         Roo.get(elid).remove();
38336                     } else if(dds[dd].config.isTarget){
38337                         dds[dd].proxyTop.remove();
38338                         dds[dd].proxyBottom.remove();
38339                         dds[dd].unreg();
38340                     }
38341                     if(Roo.dd.DDM.locationCache[dd]){
38342                         delete Roo.dd.DDM.locationCache[dd];
38343                     }
38344                 }
38345                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38346             }
38347         }
38348         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38349         this.bind(null, null);
38350         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38351     },
38352
38353     handleLockChange : function(){
38354         this.refresh(true);
38355     },
38356
38357     onDenyColumnLock : function(){
38358
38359     },
38360
38361     onDenyColumnHide : function(){
38362
38363     },
38364
38365     handleHdMenuClick : function(item){
38366         var index = this.hdCtxIndex;
38367         var cm = this.cm, ds = this.ds;
38368         switch(item.id){
38369             case "asc":
38370                 ds.sort(cm.getDataIndex(index), "ASC");
38371                 break;
38372             case "desc":
38373                 ds.sort(cm.getDataIndex(index), "DESC");
38374                 break;
38375             case "lock":
38376                 var lc = cm.getLockedCount();
38377                 if(cm.getColumnCount(true) <= lc+1){
38378                     this.onDenyColumnLock();
38379                     return;
38380                 }
38381                 if(lc != index){
38382                     cm.setLocked(index, true, true);
38383                     cm.moveColumn(index, lc);
38384                     this.grid.fireEvent("columnmove", index, lc);
38385                 }else{
38386                     cm.setLocked(index, true);
38387                 }
38388             break;
38389             case "unlock":
38390                 var lc = cm.getLockedCount();
38391                 if((lc-1) != index){
38392                     cm.setLocked(index, false, true);
38393                     cm.moveColumn(index, lc-1);
38394                     this.grid.fireEvent("columnmove", index, lc-1);
38395                 }else{
38396                     cm.setLocked(index, false);
38397                 }
38398             break;
38399             case 'wider': // used to expand cols on touch..
38400             case 'narrow':
38401                 var cw = cm.getColumnWidth(index);
38402                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38403                 cw = Math.max(0, cw);
38404                 cw = Math.min(cw,4000);
38405                 cm.setColumnWidth(index, cw);
38406                 break;
38407                 
38408             default:
38409                 index = cm.getIndexById(item.id.substr(4));
38410                 if(index != -1){
38411                     if(item.checked && cm.getColumnCount(true) <= 1){
38412                         this.onDenyColumnHide();
38413                         return false;
38414                     }
38415                     cm.setHidden(index, item.checked);
38416                 }
38417         }
38418         return true;
38419     },
38420
38421     beforeColMenuShow : function(){
38422         var cm = this.cm,  colCount = cm.getColumnCount();
38423         this.colMenu.removeAll();
38424         for(var i = 0; i < colCount; i++){
38425             this.colMenu.add(new Roo.menu.CheckItem({
38426                 id: "col-"+cm.getColumnId(i),
38427                 text: cm.getColumnHeader(i),
38428                 checked: !cm.isHidden(i),
38429                 hideOnClick:false
38430             }));
38431         }
38432     },
38433
38434     handleHdCtx : function(g, index, e){
38435         e.stopEvent();
38436         var hd = this.getHeaderCell(index);
38437         this.hdCtxIndex = index;
38438         var ms = this.hmenu.items, cm = this.cm;
38439         ms.get("asc").setDisabled(!cm.isSortable(index));
38440         ms.get("desc").setDisabled(!cm.isSortable(index));
38441         if(this.grid.enableColLock !== false){
38442             ms.get("lock").setDisabled(cm.isLocked(index));
38443             ms.get("unlock").setDisabled(!cm.isLocked(index));
38444         }
38445         this.hmenu.show(hd, "tl-bl");
38446     },
38447
38448     handleHdOver : function(e){
38449         var hd = this.findHeaderCell(e.getTarget());
38450         if(hd && !this.headersDisabled){
38451             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38452                this.fly(hd).addClass("x-grid-hd-over");
38453             }
38454         }
38455     },
38456
38457     handleHdOut : function(e){
38458         var hd = this.findHeaderCell(e.getTarget());
38459         if(hd){
38460             this.fly(hd).removeClass("x-grid-hd-over");
38461         }
38462     },
38463
38464     handleSplitDblClick : function(e, t){
38465         var i = this.getCellIndex(t);
38466         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38467             this.autoSizeColumn(i, true);
38468             this.layout();
38469         }
38470     },
38471
38472     render : function(){
38473
38474         var cm = this.cm;
38475         var colCount = cm.getColumnCount();
38476
38477         if(this.grid.monitorWindowResize === true){
38478             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38479         }
38480         var header = this.renderHeaders();
38481         var body = this.templates.body.apply({rows:""});
38482         var html = this.templates.master.apply({
38483             lockedBody: body,
38484             body: body,
38485             lockedHeader: header[0],
38486             header: header[1]
38487         });
38488
38489         //this.updateColumns();
38490
38491         this.grid.getGridEl().dom.innerHTML = html;
38492
38493         this.initElements();
38494         
38495         // a kludge to fix the random scolling effect in webkit
38496         this.el.on("scroll", function() {
38497             this.el.dom.scrollTop=0; // hopefully not recursive..
38498         },this);
38499
38500         this.scroller.on("scroll", this.handleScroll, this);
38501         this.lockedBody.on("mousewheel", this.handleWheel, this);
38502         this.mainBody.on("mousewheel", this.handleWheel, this);
38503
38504         this.mainHd.on("mouseover", this.handleHdOver, this);
38505         this.mainHd.on("mouseout", this.handleHdOut, this);
38506         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38507                 {delegate: "."+this.splitClass});
38508
38509         this.lockedHd.on("mouseover", this.handleHdOver, this);
38510         this.lockedHd.on("mouseout", this.handleHdOut, this);
38511         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38512                 {delegate: "."+this.splitClass});
38513
38514         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38515             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38516         }
38517
38518         this.updateSplitters();
38519
38520         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38521             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38522             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38523         }
38524
38525         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38526             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38527             this.hmenu.add(
38528                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38529                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38530             );
38531             if(this.grid.enableColLock !== false){
38532                 this.hmenu.add('-',
38533                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38534                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38535                 );
38536             }
38537             if (Roo.isTouch) {
38538                  this.hmenu.add('-',
38539                     {id:"wider", text: this.columnsWiderText},
38540                     {id:"narrow", text: this.columnsNarrowText }
38541                 );
38542                 
38543                  
38544             }
38545             
38546             if(this.grid.enableColumnHide !== false){
38547
38548                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38549                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38550                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38551
38552                 this.hmenu.add('-',
38553                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38554                 );
38555             }
38556             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38557
38558             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38559         }
38560
38561         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38562             this.dd = new Roo.grid.GridDragZone(this.grid, {
38563                 ddGroup : this.grid.ddGroup || 'GridDD'
38564             });
38565             
38566         }
38567
38568         /*
38569         for(var i = 0; i < colCount; i++){
38570             if(cm.isHidden(i)){
38571                 this.hideColumn(i);
38572             }
38573             if(cm.config[i].align){
38574                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38575                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38576             }
38577         }*/
38578         
38579         this.updateHeaderSortState();
38580
38581         this.beforeInitialResize();
38582         this.layout(true);
38583
38584         // two part rendering gives faster view to the user
38585         this.renderPhase2.defer(1, this);
38586     },
38587
38588     renderPhase2 : function(){
38589         // render the rows now
38590         this.refresh();
38591         if(this.grid.autoSizeColumns){
38592             this.autoSizeColumns();
38593         }
38594     },
38595
38596     beforeInitialResize : function(){
38597
38598     },
38599
38600     onColumnSplitterMoved : function(i, w){
38601         this.userResized = true;
38602         var cm = this.grid.colModel;
38603         cm.setColumnWidth(i, w, true);
38604         var cid = cm.getColumnId(i);
38605         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38606         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38607         this.updateSplitters();
38608         this.layout();
38609         this.grid.fireEvent("columnresize", i, w);
38610     },
38611
38612     syncRowHeights : function(startIndex, endIndex){
38613         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38614             startIndex = startIndex || 0;
38615             var mrows = this.getBodyTable().rows;
38616             var lrows = this.getLockedTable().rows;
38617             var len = mrows.length-1;
38618             endIndex = Math.min(endIndex || len, len);
38619             for(var i = startIndex; i <= endIndex; i++){
38620                 var m = mrows[i], l = lrows[i];
38621                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38622                 m.style.height = l.style.height = h + "px";
38623             }
38624         }
38625     },
38626
38627     layout : function(initialRender, is2ndPass){
38628         var g = this.grid;
38629         var auto = g.autoHeight;
38630         var scrollOffset = 16;
38631         var c = g.getGridEl(), cm = this.cm,
38632                 expandCol = g.autoExpandColumn,
38633                 gv = this;
38634         //c.beginMeasure();
38635
38636         if(!c.dom.offsetWidth){ // display:none?
38637             if(initialRender){
38638                 this.lockedWrap.show();
38639                 this.mainWrap.show();
38640             }
38641             return;
38642         }
38643
38644         var hasLock = this.cm.isLocked(0);
38645
38646         var tbh = this.headerPanel.getHeight();
38647         var bbh = this.footerPanel.getHeight();
38648
38649         if(auto){
38650             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38651             var newHeight = ch + c.getBorderWidth("tb");
38652             if(g.maxHeight){
38653                 newHeight = Math.min(g.maxHeight, newHeight);
38654             }
38655             c.setHeight(newHeight);
38656         }
38657
38658         if(g.autoWidth){
38659             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38660         }
38661
38662         var s = this.scroller;
38663
38664         var csize = c.getSize(true);
38665
38666         this.el.setSize(csize.width, csize.height);
38667
38668         this.headerPanel.setWidth(csize.width);
38669         this.footerPanel.setWidth(csize.width);
38670
38671         var hdHeight = this.mainHd.getHeight();
38672         var vw = csize.width;
38673         var vh = csize.height - (tbh + bbh);
38674
38675         s.setSize(vw, vh);
38676
38677         var bt = this.getBodyTable();
38678         var ltWidth = hasLock ?
38679                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38680
38681         var scrollHeight = bt.offsetHeight;
38682         var scrollWidth = ltWidth + bt.offsetWidth;
38683         var vscroll = false, hscroll = false;
38684
38685         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38686
38687         var lw = this.lockedWrap, mw = this.mainWrap;
38688         var lb = this.lockedBody, mb = this.mainBody;
38689
38690         setTimeout(function(){
38691             var t = s.dom.offsetTop;
38692             var w = s.dom.clientWidth,
38693                 h = s.dom.clientHeight;
38694
38695             lw.setTop(t);
38696             lw.setSize(ltWidth, h);
38697
38698             mw.setLeftTop(ltWidth, t);
38699             mw.setSize(w-ltWidth, h);
38700
38701             lb.setHeight(h-hdHeight);
38702             mb.setHeight(h-hdHeight);
38703
38704             if(is2ndPass !== true && !gv.userResized && expandCol){
38705                 // high speed resize without full column calculation
38706                 
38707                 var ci = cm.getIndexById(expandCol);
38708                 if (ci < 0) {
38709                     ci = cm.findColumnIndex(expandCol);
38710                 }
38711                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38712                 var expandId = cm.getColumnId(ci);
38713                 var  tw = cm.getTotalWidth(false);
38714                 var currentWidth = cm.getColumnWidth(ci);
38715                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38716                 if(currentWidth != cw){
38717                     cm.setColumnWidth(ci, cw, true);
38718                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38719                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38720                     gv.updateSplitters();
38721                     gv.layout(false, true);
38722                 }
38723             }
38724
38725             if(initialRender){
38726                 lw.show();
38727                 mw.show();
38728             }
38729             //c.endMeasure();
38730         }, 10);
38731     },
38732
38733     onWindowResize : function(){
38734         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38735             return;
38736         }
38737         this.layout();
38738     },
38739
38740     appendFooter : function(parentEl){
38741         return null;
38742     },
38743
38744     sortAscText : "Sort Ascending",
38745     sortDescText : "Sort Descending",
38746     lockText : "Lock Column",
38747     unlockText : "Unlock Column",
38748     columnsText : "Columns",
38749  
38750     columnsWiderText : "Wider",
38751     columnsNarrowText : "Thinner"
38752 });
38753
38754
38755 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38756     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38757     this.proxy.el.addClass('x-grid3-col-dd');
38758 };
38759
38760 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38761     handleMouseDown : function(e){
38762
38763     },
38764
38765     callHandleMouseDown : function(e){
38766         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38767     }
38768 });
38769 /*
38770  * Based on:
38771  * Ext JS Library 1.1.1
38772  * Copyright(c) 2006-2007, Ext JS, LLC.
38773  *
38774  * Originally Released Under LGPL - original licence link has changed is not relivant.
38775  *
38776  * Fork - LGPL
38777  * <script type="text/javascript">
38778  */
38779  
38780 // private
38781 // This is a support class used internally by the Grid components
38782 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38783     this.grid = grid;
38784     this.view = grid.getView();
38785     this.proxy = this.view.resizeProxy;
38786     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38787         "gridSplitters" + this.grid.getGridEl().id, {
38788         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38789     });
38790     this.setHandleElId(Roo.id(hd));
38791     this.setOuterHandleElId(Roo.id(hd2));
38792     this.scroll = false;
38793 };
38794 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38795     fly: Roo.Element.fly,
38796
38797     b4StartDrag : function(x, y){
38798         this.view.headersDisabled = true;
38799         this.proxy.setHeight(this.view.mainWrap.getHeight());
38800         var w = this.cm.getColumnWidth(this.cellIndex);
38801         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38802         this.resetConstraints();
38803         this.setXConstraint(minw, 1000);
38804         this.setYConstraint(0, 0);
38805         this.minX = x - minw;
38806         this.maxX = x + 1000;
38807         this.startPos = x;
38808         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38809     },
38810
38811
38812     handleMouseDown : function(e){
38813         ev = Roo.EventObject.setEvent(e);
38814         var t = this.fly(ev.getTarget());
38815         if(t.hasClass("x-grid-split")){
38816             this.cellIndex = this.view.getCellIndex(t.dom);
38817             this.split = t.dom;
38818             this.cm = this.grid.colModel;
38819             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38820                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38821             }
38822         }
38823     },
38824
38825     endDrag : function(e){
38826         this.view.headersDisabled = false;
38827         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38828         var diff = endX - this.startPos;
38829         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38830     },
38831
38832     autoOffset : function(){
38833         this.setDelta(0,0);
38834     }
38835 });/*
38836  * Based on:
38837  * Ext JS Library 1.1.1
38838  * Copyright(c) 2006-2007, Ext JS, LLC.
38839  *
38840  * Originally Released Under LGPL - original licence link has changed is not relivant.
38841  *
38842  * Fork - LGPL
38843  * <script type="text/javascript">
38844  */
38845  
38846 // private
38847 // This is a support class used internally by the Grid components
38848 Roo.grid.GridDragZone = function(grid, config){
38849     this.view = grid.getView();
38850     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38851     if(this.view.lockedBody){
38852         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38853         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38854     }
38855     this.scroll = false;
38856     this.grid = grid;
38857     this.ddel = document.createElement('div');
38858     this.ddel.className = 'x-grid-dd-wrap';
38859 };
38860
38861 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38862     ddGroup : "GridDD",
38863
38864     getDragData : function(e){
38865         var t = Roo.lib.Event.getTarget(e);
38866         var rowIndex = this.view.findRowIndex(t);
38867         var sm = this.grid.selModel;
38868             
38869         //Roo.log(rowIndex);
38870         
38871         if (sm.getSelectedCell) {
38872             // cell selection..
38873             if (!sm.getSelectedCell()) {
38874                 return false;
38875             }
38876             if (rowIndex != sm.getSelectedCell()[0]) {
38877                 return false;
38878             }
38879         
38880         }
38881         
38882         if(rowIndex !== false){
38883             
38884             // if editorgrid.. 
38885             
38886             
38887             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38888                
38889             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38890               //  
38891             //}
38892             if (e.hasModifier()){
38893                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38894             }
38895             
38896             Roo.log("getDragData");
38897             
38898             return {
38899                 grid: this.grid,
38900                 ddel: this.ddel,
38901                 rowIndex: rowIndex,
38902                 selections:sm.getSelections ? sm.getSelections() : (
38903                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38904                 )
38905             };
38906         }
38907         return false;
38908     },
38909
38910     onInitDrag : function(e){
38911         var data = this.dragData;
38912         this.ddel.innerHTML = this.grid.getDragDropText();
38913         this.proxy.update(this.ddel);
38914         // fire start drag?
38915     },
38916
38917     afterRepair : function(){
38918         this.dragging = false;
38919     },
38920
38921     getRepairXY : function(e, data){
38922         return false;
38923     },
38924
38925     onEndDrag : function(data, e){
38926         // fire end drag?
38927     },
38928
38929     onValidDrop : function(dd, e, id){
38930         // fire drag drop?
38931         this.hideProxy();
38932     },
38933
38934     beforeInvalidDrop : function(e, id){
38935
38936     }
38937 });/*
38938  * Based on:
38939  * Ext JS Library 1.1.1
38940  * Copyright(c) 2006-2007, Ext JS, LLC.
38941  *
38942  * Originally Released Under LGPL - original licence link has changed is not relivant.
38943  *
38944  * Fork - LGPL
38945  * <script type="text/javascript">
38946  */
38947  
38948
38949 /**
38950  * @class Roo.grid.ColumnModel
38951  * @extends Roo.util.Observable
38952  * This is the default implementation of a ColumnModel used by the Grid. It defines
38953  * the columns in the grid.
38954  * <br>Usage:<br>
38955  <pre><code>
38956  var colModel = new Roo.grid.ColumnModel([
38957         {header: "Ticker", width: 60, sortable: true, locked: true},
38958         {header: "Company Name", width: 150, sortable: true},
38959         {header: "Market Cap.", width: 100, sortable: true},
38960         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38961         {header: "Employees", width: 100, sortable: true, resizable: false}
38962  ]);
38963  </code></pre>
38964  * <p>
38965  
38966  * The config options listed for this class are options which may appear in each
38967  * individual column definition.
38968  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38969  * @constructor
38970  * @param {Object} config An Array of column config objects. See this class's
38971  * config objects for details.
38972 */
38973 Roo.grid.ColumnModel = function(config){
38974         /**
38975      * The config passed into the constructor
38976      */
38977     this.config = config;
38978     this.lookup = {};
38979
38980     // if no id, create one
38981     // if the column does not have a dataIndex mapping,
38982     // map it to the order it is in the config
38983     for(var i = 0, len = config.length; i < len; i++){
38984         var c = config[i];
38985         if(typeof c.dataIndex == "undefined"){
38986             c.dataIndex = i;
38987         }
38988         if(typeof c.renderer == "string"){
38989             c.renderer = Roo.util.Format[c.renderer];
38990         }
38991         if(typeof c.id == "undefined"){
38992             c.id = Roo.id();
38993         }
38994         if(c.editor && c.editor.xtype){
38995             c.editor  = Roo.factory(c.editor, Roo.grid);
38996         }
38997         if(c.editor && c.editor.isFormField){
38998             c.editor = new Roo.grid.GridEditor(c.editor);
38999         }
39000         this.lookup[c.id] = c;
39001     }
39002
39003     /**
39004      * The width of columns which have no width specified (defaults to 100)
39005      * @type Number
39006      */
39007     this.defaultWidth = 100;
39008
39009     /**
39010      * Default sortable of columns which have no sortable specified (defaults to false)
39011      * @type Boolean
39012      */
39013     this.defaultSortable = false;
39014
39015     this.addEvents({
39016         /**
39017              * @event widthchange
39018              * Fires when the width of a column changes.
39019              * @param {ColumnModel} this
39020              * @param {Number} columnIndex The column index
39021              * @param {Number} newWidth The new width
39022              */
39023             "widthchange": true,
39024         /**
39025              * @event headerchange
39026              * Fires when the text of a header changes.
39027              * @param {ColumnModel} this
39028              * @param {Number} columnIndex The column index
39029              * @param {Number} newText The new header text
39030              */
39031             "headerchange": true,
39032         /**
39033              * @event hiddenchange
39034              * Fires when a column is hidden or "unhidden".
39035              * @param {ColumnModel} this
39036              * @param {Number} columnIndex The column index
39037              * @param {Boolean} hidden true if hidden, false otherwise
39038              */
39039             "hiddenchange": true,
39040             /**
39041          * @event columnmoved
39042          * Fires when a column is moved.
39043          * @param {ColumnModel} this
39044          * @param {Number} oldIndex
39045          * @param {Number} newIndex
39046          */
39047         "columnmoved" : true,
39048         /**
39049          * @event columlockchange
39050          * Fires when a column's locked state is changed
39051          * @param {ColumnModel} this
39052          * @param {Number} colIndex
39053          * @param {Boolean} locked true if locked
39054          */
39055         "columnlockchange" : true
39056     });
39057     Roo.grid.ColumnModel.superclass.constructor.call(this);
39058 };
39059 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39060     /**
39061      * @cfg {String} header The header text to display in the Grid view.
39062      */
39063     /**
39064      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39065      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39066      * specified, the column's index is used as an index into the Record's data Array.
39067      */
39068     /**
39069      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39070      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39071      */
39072     /**
39073      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39074      * Defaults to the value of the {@link #defaultSortable} property.
39075      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39076      */
39077     /**
39078      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39079      */
39080     /**
39081      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39082      */
39083     /**
39084      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39085      */
39086     /**
39087      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39088      */
39089     /**
39090      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39091      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39092      * default renderer uses the raw data value. If an object is returned (bootstrap only)
39093      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39094      */
39095        /**
39096      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39097      */
39098     /**
39099      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39100      */
39101     /**
39102      * @cfg {String} cursor (Optional)
39103      */
39104     /**
39105      * @cfg {String} tooltip (Optional)
39106      */
39107     /**
39108      * @cfg {Number} xs (Optional)
39109      */
39110     /**
39111      * @cfg {Number} sm (Optional)
39112      */
39113     /**
39114      * @cfg {Number} md (Optional)
39115      */
39116     /**
39117      * @cfg {Number} lg (Optional)
39118      */
39119     /**
39120      * Returns the id of the column at the specified index.
39121      * @param {Number} index The column index
39122      * @return {String} the id
39123      */
39124     getColumnId : function(index){
39125         return this.config[index].id;
39126     },
39127
39128     /**
39129      * Returns the column for a specified id.
39130      * @param {String} id The column id
39131      * @return {Object} the column
39132      */
39133     getColumnById : function(id){
39134         return this.lookup[id];
39135     },
39136
39137     
39138     /**
39139      * Returns the column for a specified dataIndex.
39140      * @param {String} dataIndex The column dataIndex
39141      * @return {Object|Boolean} the column or false if not found
39142      */
39143     getColumnByDataIndex: function(dataIndex){
39144         var index = this.findColumnIndex(dataIndex);
39145         return index > -1 ? this.config[index] : false;
39146     },
39147     
39148     /**
39149      * Returns the index for a specified column id.
39150      * @param {String} id The column id
39151      * @return {Number} the index, or -1 if not found
39152      */
39153     getIndexById : function(id){
39154         for(var i = 0, len = this.config.length; i < len; i++){
39155             if(this.config[i].id == id){
39156                 return i;
39157             }
39158         }
39159         return -1;
39160     },
39161     
39162     /**
39163      * Returns the index for a specified column dataIndex.
39164      * @param {String} dataIndex The column dataIndex
39165      * @return {Number} the index, or -1 if not found
39166      */
39167     
39168     findColumnIndex : function(dataIndex){
39169         for(var i = 0, len = this.config.length; i < len; i++){
39170             if(this.config[i].dataIndex == dataIndex){
39171                 return i;
39172             }
39173         }
39174         return -1;
39175     },
39176     
39177     
39178     moveColumn : function(oldIndex, newIndex){
39179         var c = this.config[oldIndex];
39180         this.config.splice(oldIndex, 1);
39181         this.config.splice(newIndex, 0, c);
39182         this.dataMap = null;
39183         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39184     },
39185
39186     isLocked : function(colIndex){
39187         return this.config[colIndex].locked === true;
39188     },
39189
39190     setLocked : function(colIndex, value, suppressEvent){
39191         if(this.isLocked(colIndex) == value){
39192             return;
39193         }
39194         this.config[colIndex].locked = value;
39195         if(!suppressEvent){
39196             this.fireEvent("columnlockchange", this, colIndex, value);
39197         }
39198     },
39199
39200     getTotalLockedWidth : function(){
39201         var totalWidth = 0;
39202         for(var i = 0; i < this.config.length; i++){
39203             if(this.isLocked(i) && !this.isHidden(i)){
39204                 this.totalWidth += this.getColumnWidth(i);
39205             }
39206         }
39207         return totalWidth;
39208     },
39209
39210     getLockedCount : function(){
39211         for(var i = 0, len = this.config.length; i < len; i++){
39212             if(!this.isLocked(i)){
39213                 return i;
39214             }
39215         }
39216     },
39217
39218     /**
39219      * Returns the number of columns.
39220      * @return {Number}
39221      */
39222     getColumnCount : function(visibleOnly){
39223         if(visibleOnly === true){
39224             var c = 0;
39225             for(var i = 0, len = this.config.length; i < len; i++){
39226                 if(!this.isHidden(i)){
39227                     c++;
39228                 }
39229             }
39230             return c;
39231         }
39232         return this.config.length;
39233     },
39234
39235     /**
39236      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39237      * @param {Function} fn
39238      * @param {Object} scope (optional)
39239      * @return {Array} result
39240      */
39241     getColumnsBy : function(fn, scope){
39242         var r = [];
39243         for(var i = 0, len = this.config.length; i < len; i++){
39244             var c = this.config[i];
39245             if(fn.call(scope||this, c, i) === true){
39246                 r[r.length] = c;
39247             }
39248         }
39249         return r;
39250     },
39251
39252     /**
39253      * Returns true if the specified column is sortable.
39254      * @param {Number} col The column index
39255      * @return {Boolean}
39256      */
39257     isSortable : function(col){
39258         if(typeof this.config[col].sortable == "undefined"){
39259             return this.defaultSortable;
39260         }
39261         return this.config[col].sortable;
39262     },
39263
39264     /**
39265      * Returns the rendering (formatting) function defined for the column.
39266      * @param {Number} col The column index.
39267      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39268      */
39269     getRenderer : function(col){
39270         if(!this.config[col].renderer){
39271             return Roo.grid.ColumnModel.defaultRenderer;
39272         }
39273         return this.config[col].renderer;
39274     },
39275
39276     /**
39277      * Sets the rendering (formatting) function for a column.
39278      * @param {Number} col The column index
39279      * @param {Function} fn The function to use to process the cell's raw data
39280      * to return HTML markup for the grid view. The render function is called with
39281      * the following parameters:<ul>
39282      * <li>Data value.</li>
39283      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39284      * <li>css A CSS style string to apply to the table cell.</li>
39285      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39286      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39287      * <li>Row index</li>
39288      * <li>Column index</li>
39289      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39290      */
39291     setRenderer : function(col, fn){
39292         this.config[col].renderer = fn;
39293     },
39294
39295     /**
39296      * Returns the width for the specified column.
39297      * @param {Number} col The column index
39298      * @return {Number}
39299      */
39300     getColumnWidth : function(col){
39301         return this.config[col].width * 1 || this.defaultWidth;
39302     },
39303
39304     /**
39305      * Sets the width for a column.
39306      * @param {Number} col The column index
39307      * @param {Number} width The new width
39308      */
39309     setColumnWidth : function(col, width, suppressEvent){
39310         this.config[col].width = width;
39311         this.totalWidth = null;
39312         if(!suppressEvent){
39313              this.fireEvent("widthchange", this, col, width);
39314         }
39315     },
39316
39317     /**
39318      * Returns the total width of all columns.
39319      * @param {Boolean} includeHidden True to include hidden column widths
39320      * @return {Number}
39321      */
39322     getTotalWidth : function(includeHidden){
39323         if(!this.totalWidth){
39324             this.totalWidth = 0;
39325             for(var i = 0, len = this.config.length; i < len; i++){
39326                 if(includeHidden || !this.isHidden(i)){
39327                     this.totalWidth += this.getColumnWidth(i);
39328                 }
39329             }
39330         }
39331         return this.totalWidth;
39332     },
39333
39334     /**
39335      * Returns the header for the specified column.
39336      * @param {Number} col The column index
39337      * @return {String}
39338      */
39339     getColumnHeader : function(col){
39340         return this.config[col].header;
39341     },
39342
39343     /**
39344      * Sets the header for a column.
39345      * @param {Number} col The column index
39346      * @param {String} header The new header
39347      */
39348     setColumnHeader : function(col, header){
39349         this.config[col].header = header;
39350         this.fireEvent("headerchange", this, col, header);
39351     },
39352
39353     /**
39354      * Returns the tooltip for the specified column.
39355      * @param {Number} col The column index
39356      * @return {String}
39357      */
39358     getColumnTooltip : function(col){
39359             return this.config[col].tooltip;
39360     },
39361     /**
39362      * Sets the tooltip for a column.
39363      * @param {Number} col The column index
39364      * @param {String} tooltip The new tooltip
39365      */
39366     setColumnTooltip : function(col, tooltip){
39367             this.config[col].tooltip = tooltip;
39368     },
39369
39370     /**
39371      * Returns the dataIndex for the specified column.
39372      * @param {Number} col The column index
39373      * @return {Number}
39374      */
39375     getDataIndex : function(col){
39376         return this.config[col].dataIndex;
39377     },
39378
39379     /**
39380      * Sets the dataIndex for a column.
39381      * @param {Number} col The column index
39382      * @param {Number} dataIndex The new dataIndex
39383      */
39384     setDataIndex : function(col, dataIndex){
39385         this.config[col].dataIndex = dataIndex;
39386     },
39387
39388     
39389     
39390     /**
39391      * Returns true if the cell is editable.
39392      * @param {Number} colIndex The column index
39393      * @param {Number} rowIndex The row index - this is nto actually used..?
39394      * @return {Boolean}
39395      */
39396     isCellEditable : function(colIndex, rowIndex){
39397         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39398     },
39399
39400     /**
39401      * Returns the editor defined for the cell/column.
39402      * return false or null to disable editing.
39403      * @param {Number} colIndex The column index
39404      * @param {Number} rowIndex The row index
39405      * @return {Object}
39406      */
39407     getCellEditor : function(colIndex, rowIndex){
39408         return this.config[colIndex].editor;
39409     },
39410
39411     /**
39412      * Sets if a column is editable.
39413      * @param {Number} col The column index
39414      * @param {Boolean} editable True if the column is editable
39415      */
39416     setEditable : function(col, editable){
39417         this.config[col].editable = editable;
39418     },
39419
39420
39421     /**
39422      * Returns true if the column is hidden.
39423      * @param {Number} colIndex The column index
39424      * @return {Boolean}
39425      */
39426     isHidden : function(colIndex){
39427         return this.config[colIndex].hidden;
39428     },
39429
39430
39431     /**
39432      * Returns true if the column width cannot be changed
39433      */
39434     isFixed : function(colIndex){
39435         return this.config[colIndex].fixed;
39436     },
39437
39438     /**
39439      * Returns true if the column can be resized
39440      * @return {Boolean}
39441      */
39442     isResizable : function(colIndex){
39443         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39444     },
39445     /**
39446      * Sets if a column is hidden.
39447      * @param {Number} colIndex The column index
39448      * @param {Boolean} hidden True if the column is hidden
39449      */
39450     setHidden : function(colIndex, hidden){
39451         this.config[colIndex].hidden = hidden;
39452         this.totalWidth = null;
39453         this.fireEvent("hiddenchange", this, colIndex, hidden);
39454     },
39455
39456     /**
39457      * Sets the editor for a column.
39458      * @param {Number} col The column index
39459      * @param {Object} editor The editor object
39460      */
39461     setEditor : function(col, editor){
39462         this.config[col].editor = editor;
39463     }
39464 });
39465
39466 Roo.grid.ColumnModel.defaultRenderer = function(value){
39467         if(typeof value == "string" && value.length < 1){
39468             return "&#160;";
39469         }
39470         return value;
39471 };
39472
39473 // Alias for backwards compatibility
39474 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39475 /*
39476  * Based on:
39477  * Ext JS Library 1.1.1
39478  * Copyright(c) 2006-2007, Ext JS, LLC.
39479  *
39480  * Originally Released Under LGPL - original licence link has changed is not relivant.
39481  *
39482  * Fork - LGPL
39483  * <script type="text/javascript">
39484  */
39485
39486 /**
39487  * @class Roo.grid.AbstractSelectionModel
39488  * @extends Roo.util.Observable
39489  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39490  * implemented by descendant classes.  This class should not be directly instantiated.
39491  * @constructor
39492  */
39493 Roo.grid.AbstractSelectionModel = function(){
39494     this.locked = false;
39495     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39496 };
39497
39498 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39499     /** @ignore Called by the grid automatically. Do not call directly. */
39500     init : function(grid){
39501         this.grid = grid;
39502         this.initEvents();
39503     },
39504
39505     /**
39506      * Locks the selections.
39507      */
39508     lock : function(){
39509         this.locked = true;
39510     },
39511
39512     /**
39513      * Unlocks the selections.
39514      */
39515     unlock : function(){
39516         this.locked = false;
39517     },
39518
39519     /**
39520      * Returns true if the selections are locked.
39521      * @return {Boolean}
39522      */
39523     isLocked : function(){
39524         return this.locked;
39525     }
39526 });/*
39527  * Based on:
39528  * Ext JS Library 1.1.1
39529  * Copyright(c) 2006-2007, Ext JS, LLC.
39530  *
39531  * Originally Released Under LGPL - original licence link has changed is not relivant.
39532  *
39533  * Fork - LGPL
39534  * <script type="text/javascript">
39535  */
39536 /**
39537  * @extends Roo.grid.AbstractSelectionModel
39538  * @class Roo.grid.RowSelectionModel
39539  * The default SelectionModel used by {@link Roo.grid.Grid}.
39540  * It supports multiple selections and keyboard selection/navigation. 
39541  * @constructor
39542  * @param {Object} config
39543  */
39544 Roo.grid.RowSelectionModel = function(config){
39545     Roo.apply(this, config);
39546     this.selections = new Roo.util.MixedCollection(false, function(o){
39547         return o.id;
39548     });
39549
39550     this.last = false;
39551     this.lastActive = false;
39552
39553     this.addEvents({
39554         /**
39555              * @event selectionchange
39556              * Fires when the selection changes
39557              * @param {SelectionModel} this
39558              */
39559             "selectionchange" : true,
39560         /**
39561              * @event afterselectionchange
39562              * Fires after the selection changes (eg. by key press or clicking)
39563              * @param {SelectionModel} this
39564              */
39565             "afterselectionchange" : true,
39566         /**
39567              * @event beforerowselect
39568              * Fires when a row is selected being selected, return false to cancel.
39569              * @param {SelectionModel} this
39570              * @param {Number} rowIndex The selected index
39571              * @param {Boolean} keepExisting False if other selections will be cleared
39572              */
39573             "beforerowselect" : true,
39574         /**
39575              * @event rowselect
39576              * Fires when a row is selected.
39577              * @param {SelectionModel} this
39578              * @param {Number} rowIndex The selected index
39579              * @param {Roo.data.Record} r The record
39580              */
39581             "rowselect" : true,
39582         /**
39583              * @event rowdeselect
39584              * Fires when a row is deselected.
39585              * @param {SelectionModel} this
39586              * @param {Number} rowIndex The selected index
39587              */
39588         "rowdeselect" : true
39589     });
39590     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39591     this.locked = false;
39592 };
39593
39594 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39595     /**
39596      * @cfg {Boolean} singleSelect
39597      * True to allow selection of only one row at a time (defaults to false)
39598      */
39599     singleSelect : false,
39600
39601     // private
39602     initEvents : function(){
39603
39604         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39605             this.grid.on("mousedown", this.handleMouseDown, this);
39606         }else{ // allow click to work like normal
39607             this.grid.on("rowclick", this.handleDragableRowClick, this);
39608         }
39609
39610         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39611             "up" : function(e){
39612                 if(!e.shiftKey){
39613                     this.selectPrevious(e.shiftKey);
39614                 }else if(this.last !== false && this.lastActive !== false){
39615                     var last = this.last;
39616                     this.selectRange(this.last,  this.lastActive-1);
39617                     this.grid.getView().focusRow(this.lastActive);
39618                     if(last !== false){
39619                         this.last = last;
39620                     }
39621                 }else{
39622                     this.selectFirstRow();
39623                 }
39624                 this.fireEvent("afterselectionchange", this);
39625             },
39626             "down" : function(e){
39627                 if(!e.shiftKey){
39628                     this.selectNext(e.shiftKey);
39629                 }else if(this.last !== false && this.lastActive !== false){
39630                     var last = this.last;
39631                     this.selectRange(this.last,  this.lastActive+1);
39632                     this.grid.getView().focusRow(this.lastActive);
39633                     if(last !== false){
39634                         this.last = last;
39635                     }
39636                 }else{
39637                     this.selectFirstRow();
39638                 }
39639                 this.fireEvent("afterselectionchange", this);
39640             },
39641             scope: this
39642         });
39643
39644         var view = this.grid.view;
39645         view.on("refresh", this.onRefresh, this);
39646         view.on("rowupdated", this.onRowUpdated, this);
39647         view.on("rowremoved", this.onRemove, this);
39648     },
39649
39650     // private
39651     onRefresh : function(){
39652         var ds = this.grid.dataSource, i, v = this.grid.view;
39653         var s = this.selections;
39654         s.each(function(r){
39655             if((i = ds.indexOfId(r.id)) != -1){
39656                 v.onRowSelect(i);
39657                 s.add(ds.getAt(i)); // updating the selection relate data
39658             }else{
39659                 s.remove(r);
39660             }
39661         });
39662     },
39663
39664     // private
39665     onRemove : function(v, index, r){
39666         this.selections.remove(r);
39667     },
39668
39669     // private
39670     onRowUpdated : function(v, index, r){
39671         if(this.isSelected(r)){
39672             v.onRowSelect(index);
39673         }
39674     },
39675
39676     /**
39677      * Select records.
39678      * @param {Array} records The records to select
39679      * @param {Boolean} keepExisting (optional) True to keep existing selections
39680      */
39681     selectRecords : function(records, keepExisting){
39682         if(!keepExisting){
39683             this.clearSelections();
39684         }
39685         var ds = this.grid.dataSource;
39686         for(var i = 0, len = records.length; i < len; i++){
39687             this.selectRow(ds.indexOf(records[i]), true);
39688         }
39689     },
39690
39691     /**
39692      * Gets the number of selected rows.
39693      * @return {Number}
39694      */
39695     getCount : function(){
39696         return this.selections.length;
39697     },
39698
39699     /**
39700      * Selects the first row in the grid.
39701      */
39702     selectFirstRow : function(){
39703         this.selectRow(0);
39704     },
39705
39706     /**
39707      * Select the last row.
39708      * @param {Boolean} keepExisting (optional) True to keep existing selections
39709      */
39710     selectLastRow : function(keepExisting){
39711         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39712     },
39713
39714     /**
39715      * Selects the row immediately following the last selected row.
39716      * @param {Boolean} keepExisting (optional) True to keep existing selections
39717      */
39718     selectNext : function(keepExisting){
39719         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39720             this.selectRow(this.last+1, keepExisting);
39721             this.grid.getView().focusRow(this.last);
39722         }
39723     },
39724
39725     /**
39726      * Selects the row that precedes the last selected row.
39727      * @param {Boolean} keepExisting (optional) True to keep existing selections
39728      */
39729     selectPrevious : function(keepExisting){
39730         if(this.last){
39731             this.selectRow(this.last-1, keepExisting);
39732             this.grid.getView().focusRow(this.last);
39733         }
39734     },
39735
39736     /**
39737      * Returns the selected records
39738      * @return {Array} Array of selected records
39739      */
39740     getSelections : function(){
39741         return [].concat(this.selections.items);
39742     },
39743
39744     /**
39745      * Returns the first selected record.
39746      * @return {Record}
39747      */
39748     getSelected : function(){
39749         return this.selections.itemAt(0);
39750     },
39751
39752
39753     /**
39754      * Clears all selections.
39755      */
39756     clearSelections : function(fast){
39757         if(this.locked) {
39758             return;
39759         }
39760         if(fast !== true){
39761             var ds = this.grid.dataSource;
39762             var s = this.selections;
39763             s.each(function(r){
39764                 this.deselectRow(ds.indexOfId(r.id));
39765             }, this);
39766             s.clear();
39767         }else{
39768             this.selections.clear();
39769         }
39770         this.last = false;
39771     },
39772
39773
39774     /**
39775      * Selects all rows.
39776      */
39777     selectAll : function(){
39778         if(this.locked) {
39779             return;
39780         }
39781         this.selections.clear();
39782         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39783             this.selectRow(i, true);
39784         }
39785     },
39786
39787     /**
39788      * Returns True if there is a selection.
39789      * @return {Boolean}
39790      */
39791     hasSelection : function(){
39792         return this.selections.length > 0;
39793     },
39794
39795     /**
39796      * Returns True if the specified row is selected.
39797      * @param {Number/Record} record The record or index of the record to check
39798      * @return {Boolean}
39799      */
39800     isSelected : function(index){
39801         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39802         return (r && this.selections.key(r.id) ? true : false);
39803     },
39804
39805     /**
39806      * Returns True if the specified record id is selected.
39807      * @param {String} id The id of record to check
39808      * @return {Boolean}
39809      */
39810     isIdSelected : function(id){
39811         return (this.selections.key(id) ? true : false);
39812     },
39813
39814     // private
39815     handleMouseDown : function(e, t){
39816         var view = this.grid.getView(), rowIndex;
39817         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39818             return;
39819         };
39820         if(e.shiftKey && this.last !== false){
39821             var last = this.last;
39822             this.selectRange(last, rowIndex, e.ctrlKey);
39823             this.last = last; // reset the last
39824             view.focusRow(rowIndex);
39825         }else{
39826             var isSelected = this.isSelected(rowIndex);
39827             if(e.button !== 0 && isSelected){
39828                 view.focusRow(rowIndex);
39829             }else if(e.ctrlKey && isSelected){
39830                 this.deselectRow(rowIndex);
39831             }else if(!isSelected){
39832                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39833                 view.focusRow(rowIndex);
39834             }
39835         }
39836         this.fireEvent("afterselectionchange", this);
39837     },
39838     // private
39839     handleDragableRowClick :  function(grid, rowIndex, e) 
39840     {
39841         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39842             this.selectRow(rowIndex, false);
39843             grid.view.focusRow(rowIndex);
39844              this.fireEvent("afterselectionchange", this);
39845         }
39846     },
39847     
39848     /**
39849      * Selects multiple rows.
39850      * @param {Array} rows Array of the indexes of the row to select
39851      * @param {Boolean} keepExisting (optional) True to keep existing selections
39852      */
39853     selectRows : function(rows, keepExisting){
39854         if(!keepExisting){
39855             this.clearSelections();
39856         }
39857         for(var i = 0, len = rows.length; i < len; i++){
39858             this.selectRow(rows[i], true);
39859         }
39860     },
39861
39862     /**
39863      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39864      * @param {Number} startRow The index of the first row in the range
39865      * @param {Number} endRow The index of the last row in the range
39866      * @param {Boolean} keepExisting (optional) True to retain existing selections
39867      */
39868     selectRange : function(startRow, endRow, keepExisting){
39869         if(this.locked) {
39870             return;
39871         }
39872         if(!keepExisting){
39873             this.clearSelections();
39874         }
39875         if(startRow <= endRow){
39876             for(var i = startRow; i <= endRow; i++){
39877                 this.selectRow(i, true);
39878             }
39879         }else{
39880             for(var i = startRow; i >= endRow; i--){
39881                 this.selectRow(i, true);
39882             }
39883         }
39884     },
39885
39886     /**
39887      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39888      * @param {Number} startRow The index of the first row in the range
39889      * @param {Number} endRow The index of the last row in the range
39890      */
39891     deselectRange : function(startRow, endRow, preventViewNotify){
39892         if(this.locked) {
39893             return;
39894         }
39895         for(var i = startRow; i <= endRow; i++){
39896             this.deselectRow(i, preventViewNotify);
39897         }
39898     },
39899
39900     /**
39901      * Selects a row.
39902      * @param {Number} row The index of the row to select
39903      * @param {Boolean} keepExisting (optional) True to keep existing selections
39904      */
39905     selectRow : function(index, keepExisting, preventViewNotify){
39906         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39907             return;
39908         }
39909         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39910             if(!keepExisting || this.singleSelect){
39911                 this.clearSelections();
39912             }
39913             var r = this.grid.dataSource.getAt(index);
39914             this.selections.add(r);
39915             this.last = this.lastActive = index;
39916             if(!preventViewNotify){
39917                 this.grid.getView().onRowSelect(index);
39918             }
39919             this.fireEvent("rowselect", this, index, r);
39920             this.fireEvent("selectionchange", this);
39921         }
39922     },
39923
39924     /**
39925      * Deselects a row.
39926      * @param {Number} row The index of the row to deselect
39927      */
39928     deselectRow : function(index, preventViewNotify){
39929         if(this.locked) {
39930             return;
39931         }
39932         if(this.last == index){
39933             this.last = false;
39934         }
39935         if(this.lastActive == index){
39936             this.lastActive = false;
39937         }
39938         var r = this.grid.dataSource.getAt(index);
39939         this.selections.remove(r);
39940         if(!preventViewNotify){
39941             this.grid.getView().onRowDeselect(index);
39942         }
39943         this.fireEvent("rowdeselect", this, index);
39944         this.fireEvent("selectionchange", this);
39945     },
39946
39947     // private
39948     restoreLast : function(){
39949         if(this._last){
39950             this.last = this._last;
39951         }
39952     },
39953
39954     // private
39955     acceptsNav : function(row, col, cm){
39956         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39957     },
39958
39959     // private
39960     onEditorKey : function(field, e){
39961         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39962         if(k == e.TAB){
39963             e.stopEvent();
39964             ed.completeEdit();
39965             if(e.shiftKey){
39966                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39967             }else{
39968                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39969             }
39970         }else if(k == e.ENTER && !e.ctrlKey){
39971             e.stopEvent();
39972             ed.completeEdit();
39973             if(e.shiftKey){
39974                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39975             }else{
39976                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39977             }
39978         }else if(k == e.ESC){
39979             ed.cancelEdit();
39980         }
39981         if(newCell){
39982             g.startEditing(newCell[0], newCell[1]);
39983         }
39984     }
39985 });/*
39986  * Based on:
39987  * Ext JS Library 1.1.1
39988  * Copyright(c) 2006-2007, Ext JS, LLC.
39989  *
39990  * Originally Released Under LGPL - original licence link has changed is not relivant.
39991  *
39992  * Fork - LGPL
39993  * <script type="text/javascript">
39994  */
39995 /**
39996  * @class Roo.grid.CellSelectionModel
39997  * @extends Roo.grid.AbstractSelectionModel
39998  * This class provides the basic implementation for cell selection in a grid.
39999  * @constructor
40000  * @param {Object} config The object containing the configuration of this model.
40001  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40002  */
40003 Roo.grid.CellSelectionModel = function(config){
40004     Roo.apply(this, config);
40005
40006     this.selection = null;
40007
40008     this.addEvents({
40009         /**
40010              * @event beforerowselect
40011              * Fires before a cell is selected.
40012              * @param {SelectionModel} this
40013              * @param {Number} rowIndex The selected row index
40014              * @param {Number} colIndex The selected cell index
40015              */
40016             "beforecellselect" : true,
40017         /**
40018              * @event cellselect
40019              * Fires when a cell is selected.
40020              * @param {SelectionModel} this
40021              * @param {Number} rowIndex The selected row index
40022              * @param {Number} colIndex The selected cell index
40023              */
40024             "cellselect" : true,
40025         /**
40026              * @event selectionchange
40027              * Fires when the active selection changes.
40028              * @param {SelectionModel} this
40029              * @param {Object} selection null for no selection or an object (o) with two properties
40030                 <ul>
40031                 <li>o.record: the record object for the row the selection is in</li>
40032                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40033                 </ul>
40034              */
40035             "selectionchange" : true,
40036         /**
40037              * @event tabend
40038              * Fires when the tab (or enter) was pressed on the last editable cell
40039              * You can use this to trigger add new row.
40040              * @param {SelectionModel} this
40041              */
40042             "tabend" : true,
40043          /**
40044              * @event beforeeditnext
40045              * Fires before the next editable sell is made active
40046              * You can use this to skip to another cell or fire the tabend
40047              *    if you set cell to false
40048              * @param {Object} eventdata object : { cell : [ row, col ] } 
40049              */
40050             "beforeeditnext" : true
40051     });
40052     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40053 };
40054
40055 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40056     
40057     enter_is_tab: false,
40058
40059     /** @ignore */
40060     initEvents : function(){
40061         this.grid.on("mousedown", this.handleMouseDown, this);
40062         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40063         var view = this.grid.view;
40064         view.on("refresh", this.onViewChange, this);
40065         view.on("rowupdated", this.onRowUpdated, this);
40066         view.on("beforerowremoved", this.clearSelections, this);
40067         view.on("beforerowsinserted", this.clearSelections, this);
40068         if(this.grid.isEditor){
40069             this.grid.on("beforeedit", this.beforeEdit,  this);
40070         }
40071     },
40072
40073         //private
40074     beforeEdit : function(e){
40075         this.select(e.row, e.column, false, true, e.record);
40076     },
40077
40078         //private
40079     onRowUpdated : function(v, index, r){
40080         if(this.selection && this.selection.record == r){
40081             v.onCellSelect(index, this.selection.cell[1]);
40082         }
40083     },
40084
40085         //private
40086     onViewChange : function(){
40087         this.clearSelections(true);
40088     },
40089
40090         /**
40091          * Returns the currently selected cell,.
40092          * @return {Array} The selected cell (row, column) or null if none selected.
40093          */
40094     getSelectedCell : function(){
40095         return this.selection ? this.selection.cell : null;
40096     },
40097
40098     /**
40099      * Clears all selections.
40100      * @param {Boolean} true to prevent the gridview from being notified about the change.
40101      */
40102     clearSelections : function(preventNotify){
40103         var s = this.selection;
40104         if(s){
40105             if(preventNotify !== true){
40106                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40107             }
40108             this.selection = null;
40109             this.fireEvent("selectionchange", this, null);
40110         }
40111     },
40112
40113     /**
40114      * Returns true if there is a selection.
40115      * @return {Boolean}
40116      */
40117     hasSelection : function(){
40118         return this.selection ? true : false;
40119     },
40120
40121     /** @ignore */
40122     handleMouseDown : function(e, t){
40123         var v = this.grid.getView();
40124         if(this.isLocked()){
40125             return;
40126         };
40127         var row = v.findRowIndex(t);
40128         var cell = v.findCellIndex(t);
40129         if(row !== false && cell !== false){
40130             this.select(row, cell);
40131         }
40132     },
40133
40134     /**
40135      * Selects a cell.
40136      * @param {Number} rowIndex
40137      * @param {Number} collIndex
40138      */
40139     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40140         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40141             this.clearSelections();
40142             r = r || this.grid.dataSource.getAt(rowIndex);
40143             this.selection = {
40144                 record : r,
40145                 cell : [rowIndex, colIndex]
40146             };
40147             if(!preventViewNotify){
40148                 var v = this.grid.getView();
40149                 v.onCellSelect(rowIndex, colIndex);
40150                 if(preventFocus !== true){
40151                     v.focusCell(rowIndex, colIndex);
40152                 }
40153             }
40154             this.fireEvent("cellselect", this, rowIndex, colIndex);
40155             this.fireEvent("selectionchange", this, this.selection);
40156         }
40157     },
40158
40159         //private
40160     isSelectable : function(rowIndex, colIndex, cm){
40161         return !cm.isHidden(colIndex);
40162     },
40163
40164     /** @ignore */
40165     handleKeyDown : function(e){
40166         //Roo.log('Cell Sel Model handleKeyDown');
40167         if(!e.isNavKeyPress()){
40168             return;
40169         }
40170         var g = this.grid, s = this.selection;
40171         if(!s){
40172             e.stopEvent();
40173             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40174             if(cell){
40175                 this.select(cell[0], cell[1]);
40176             }
40177             return;
40178         }
40179         var sm = this;
40180         var walk = function(row, col, step){
40181             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40182         };
40183         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40184         var newCell;
40185
40186       
40187
40188         switch(k){
40189             case e.TAB:
40190                 // handled by onEditorKey
40191                 if (g.isEditor && g.editing) {
40192                     return;
40193                 }
40194                 if(e.shiftKey) {
40195                     newCell = walk(r, c-1, -1);
40196                 } else {
40197                     newCell = walk(r, c+1, 1);
40198                 }
40199                 break;
40200             
40201             case e.DOWN:
40202                newCell = walk(r+1, c, 1);
40203                 break;
40204             
40205             case e.UP:
40206                 newCell = walk(r-1, c, -1);
40207                 break;
40208             
40209             case e.RIGHT:
40210                 newCell = walk(r, c+1, 1);
40211                 break;
40212             
40213             case e.LEFT:
40214                 newCell = walk(r, c-1, -1);
40215                 break;
40216             
40217             case e.ENTER:
40218                 
40219                 if(g.isEditor && !g.editing){
40220                    g.startEditing(r, c);
40221                    e.stopEvent();
40222                    return;
40223                 }
40224                 
40225                 
40226              break;
40227         };
40228         if(newCell){
40229             this.select(newCell[0], newCell[1]);
40230             e.stopEvent();
40231             
40232         }
40233     },
40234
40235     acceptsNav : function(row, col, cm){
40236         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40237     },
40238     /**
40239      * Selects a cell.
40240      * @param {Number} field (not used) - as it's normally used as a listener
40241      * @param {Number} e - event - fake it by using
40242      *
40243      * var e = Roo.EventObjectImpl.prototype;
40244      * e.keyCode = e.TAB
40245      *
40246      * 
40247      */
40248     onEditorKey : function(field, e){
40249         
40250         var k = e.getKey(),
40251             newCell,
40252             g = this.grid,
40253             ed = g.activeEditor,
40254             forward = false;
40255         ///Roo.log('onEditorKey' + k);
40256         
40257         
40258         if (this.enter_is_tab && k == e.ENTER) {
40259             k = e.TAB;
40260         }
40261         
40262         if(k == e.TAB){
40263             if(e.shiftKey){
40264                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40265             }else{
40266                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40267                 forward = true;
40268             }
40269             
40270             e.stopEvent();
40271             
40272         } else if(k == e.ENTER &&  !e.ctrlKey){
40273             ed.completeEdit();
40274             e.stopEvent();
40275             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40276         
40277                 } else if(k == e.ESC){
40278             ed.cancelEdit();
40279         }
40280                 
40281         if (newCell) {
40282             var ecall = { cell : newCell, forward : forward };
40283             this.fireEvent('beforeeditnext', ecall );
40284             newCell = ecall.cell;
40285                         forward = ecall.forward;
40286         }
40287                 
40288         if(newCell){
40289             //Roo.log('next cell after edit');
40290             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40291         } else if (forward) {
40292             // tabbed past last
40293             this.fireEvent.defer(100, this, ['tabend',this]);
40294         }
40295     }
40296 });/*
40297  * Based on:
40298  * Ext JS Library 1.1.1
40299  * Copyright(c) 2006-2007, Ext JS, LLC.
40300  *
40301  * Originally Released Under LGPL - original licence link has changed is not relivant.
40302  *
40303  * Fork - LGPL
40304  * <script type="text/javascript">
40305  */
40306  
40307 /**
40308  * @class Roo.grid.EditorGrid
40309  * @extends Roo.grid.Grid
40310  * Class for creating and editable grid.
40311  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40312  * The container MUST have some type of size defined for the grid to fill. The container will be 
40313  * automatically set to position relative if it isn't already.
40314  * @param {Object} dataSource The data model to bind to
40315  * @param {Object} colModel The column model with info about this grid's columns
40316  */
40317 Roo.grid.EditorGrid = function(container, config){
40318     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40319     this.getGridEl().addClass("xedit-grid");
40320
40321     if(!this.selModel){
40322         this.selModel = new Roo.grid.CellSelectionModel();
40323     }
40324
40325     this.activeEditor = null;
40326
40327         this.addEvents({
40328             /**
40329              * @event beforeedit
40330              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40331              * <ul style="padding:5px;padding-left:16px;">
40332              * <li>grid - This grid</li>
40333              * <li>record - The record being edited</li>
40334              * <li>field - The field name being edited</li>
40335              * <li>value - The value for the field being edited.</li>
40336              * <li>row - The grid row index</li>
40337              * <li>column - The grid column index</li>
40338              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40339              * </ul>
40340              * @param {Object} e An edit event (see above for description)
40341              */
40342             "beforeedit" : true,
40343             /**
40344              * @event afteredit
40345              * Fires after a cell is edited. <br />
40346              * <ul style="padding:5px;padding-left:16px;">
40347              * <li>grid - This grid</li>
40348              * <li>record - The record being edited</li>
40349              * <li>field - The field name being edited</li>
40350              * <li>value - The value being set</li>
40351              * <li>originalValue - The original value for the field, before the edit.</li>
40352              * <li>row - The grid row index</li>
40353              * <li>column - The grid column index</li>
40354              * </ul>
40355              * @param {Object} e An edit event (see above for description)
40356              */
40357             "afteredit" : true,
40358             /**
40359              * @event validateedit
40360              * Fires after a cell is edited, but before the value is set in the record. 
40361          * You can use this to modify the value being set in the field, Return false
40362              * to cancel the change. The edit event object has the following properties <br />
40363              * <ul style="padding:5px;padding-left:16px;">
40364          * <li>editor - This editor</li>
40365              * <li>grid - This grid</li>
40366              * <li>record - The record being edited</li>
40367              * <li>field - The field name being edited</li>
40368              * <li>value - The value being set</li>
40369              * <li>originalValue - The original value for the field, before the edit.</li>
40370              * <li>row - The grid row index</li>
40371              * <li>column - The grid column index</li>
40372              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40373              * </ul>
40374              * @param {Object} e An edit event (see above for description)
40375              */
40376             "validateedit" : true
40377         });
40378     this.on("bodyscroll", this.stopEditing,  this);
40379     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40380 };
40381
40382 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40383     /**
40384      * @cfg {Number} clicksToEdit
40385      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40386      */
40387     clicksToEdit: 2,
40388
40389     // private
40390     isEditor : true,
40391     // private
40392     trackMouseOver: false, // causes very odd FF errors
40393
40394     onCellDblClick : function(g, row, col){
40395         this.startEditing(row, col);
40396     },
40397
40398     onEditComplete : function(ed, value, startValue){
40399         this.editing = false;
40400         this.activeEditor = null;
40401         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40402         var r = ed.record;
40403         var field = this.colModel.getDataIndex(ed.col);
40404         var e = {
40405             grid: this,
40406             record: r,
40407             field: field,
40408             originalValue: startValue,
40409             value: value,
40410             row: ed.row,
40411             column: ed.col,
40412             cancel:false,
40413             editor: ed
40414         };
40415         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40416         cell.show();
40417           
40418         if(String(value) !== String(startValue)){
40419             
40420             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40421                 r.set(field, e.value);
40422                 // if we are dealing with a combo box..
40423                 // then we also set the 'name' colum to be the displayField
40424                 if (ed.field.displayField && ed.field.name) {
40425                     r.set(ed.field.name, ed.field.el.dom.value);
40426                 }
40427                 
40428                 delete e.cancel; //?? why!!!
40429                 this.fireEvent("afteredit", e);
40430             }
40431         } else {
40432             this.fireEvent("afteredit", e); // always fire it!
40433         }
40434         this.view.focusCell(ed.row, ed.col);
40435     },
40436
40437     /**
40438      * Starts editing the specified for the specified row/column
40439      * @param {Number} rowIndex
40440      * @param {Number} colIndex
40441      */
40442     startEditing : function(row, col){
40443         this.stopEditing();
40444         if(this.colModel.isCellEditable(col, row)){
40445             this.view.ensureVisible(row, col, true);
40446           
40447             var r = this.dataSource.getAt(row);
40448             var field = this.colModel.getDataIndex(col);
40449             var cell = Roo.get(this.view.getCell(row,col));
40450             var e = {
40451                 grid: this,
40452                 record: r,
40453                 field: field,
40454                 value: r.data[field],
40455                 row: row,
40456                 column: col,
40457                 cancel:false 
40458             };
40459             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40460                 this.editing = true;
40461                 var ed = this.colModel.getCellEditor(col, row);
40462                 
40463                 if (!ed) {
40464                     return;
40465                 }
40466                 if(!ed.rendered){
40467                     ed.render(ed.parentEl || document.body);
40468                 }
40469                 ed.field.reset();
40470                
40471                 cell.hide();
40472                 
40473                 (function(){ // complex but required for focus issues in safari, ie and opera
40474                     ed.row = row;
40475                     ed.col = col;
40476                     ed.record = r;
40477                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40478                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40479                     this.activeEditor = ed;
40480                     var v = r.data[field];
40481                     ed.startEdit(this.view.getCell(row, col), v);
40482                     // combo's with 'displayField and name set
40483                     if (ed.field.displayField && ed.field.name) {
40484                         ed.field.el.dom.value = r.data[ed.field.name];
40485                     }
40486                     
40487                     
40488                 }).defer(50, this);
40489             }
40490         }
40491     },
40492         
40493     /**
40494      * Stops any active editing
40495      */
40496     stopEditing : function(){
40497         if(this.activeEditor){
40498             this.activeEditor.completeEdit();
40499         }
40500         this.activeEditor = null;
40501     },
40502         
40503          /**
40504      * Called to get grid's drag proxy text, by default returns this.ddText.
40505      * @return {String}
40506      */
40507     getDragDropText : function(){
40508         var count = this.selModel.getSelectedCell() ? 1 : 0;
40509         return String.format(this.ddText, count, count == 1 ? '' : 's');
40510     }
40511         
40512 });/*
40513  * Based on:
40514  * Ext JS Library 1.1.1
40515  * Copyright(c) 2006-2007, Ext JS, LLC.
40516  *
40517  * Originally Released Under LGPL - original licence link has changed is not relivant.
40518  *
40519  * Fork - LGPL
40520  * <script type="text/javascript">
40521  */
40522
40523 // private - not really -- you end up using it !
40524 // This is a support class used internally by the Grid components
40525
40526 /**
40527  * @class Roo.grid.GridEditor
40528  * @extends Roo.Editor
40529  * Class for creating and editable grid elements.
40530  * @param {Object} config any settings (must include field)
40531  */
40532 Roo.grid.GridEditor = function(field, config){
40533     if (!config && field.field) {
40534         config = field;
40535         field = Roo.factory(config.field, Roo.form);
40536     }
40537     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40538     field.monitorTab = false;
40539 };
40540
40541 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40542     
40543     /**
40544      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40545      */
40546     
40547     alignment: "tl-tl",
40548     autoSize: "width",
40549     hideEl : false,
40550     cls: "x-small-editor x-grid-editor",
40551     shim:false,
40552     shadow:"frame"
40553 });/*
40554  * Based on:
40555  * Ext JS Library 1.1.1
40556  * Copyright(c) 2006-2007, Ext JS, LLC.
40557  *
40558  * Originally Released Under LGPL - original licence link has changed is not relivant.
40559  *
40560  * Fork - LGPL
40561  * <script type="text/javascript">
40562  */
40563   
40564
40565   
40566 Roo.grid.PropertyRecord = Roo.data.Record.create([
40567     {name:'name',type:'string'},  'value'
40568 ]);
40569
40570
40571 Roo.grid.PropertyStore = function(grid, source){
40572     this.grid = grid;
40573     this.store = new Roo.data.Store({
40574         recordType : Roo.grid.PropertyRecord
40575     });
40576     this.store.on('update', this.onUpdate,  this);
40577     if(source){
40578         this.setSource(source);
40579     }
40580     Roo.grid.PropertyStore.superclass.constructor.call(this);
40581 };
40582
40583
40584
40585 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40586     setSource : function(o){
40587         this.source = o;
40588         this.store.removeAll();
40589         var data = [];
40590         for(var k in o){
40591             if(this.isEditableValue(o[k])){
40592                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40593             }
40594         }
40595         this.store.loadRecords({records: data}, {}, true);
40596     },
40597
40598     onUpdate : function(ds, record, type){
40599         if(type == Roo.data.Record.EDIT){
40600             var v = record.data['value'];
40601             var oldValue = record.modified['value'];
40602             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40603                 this.source[record.id] = v;
40604                 record.commit();
40605                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40606             }else{
40607                 record.reject();
40608             }
40609         }
40610     },
40611
40612     getProperty : function(row){
40613        return this.store.getAt(row);
40614     },
40615
40616     isEditableValue: function(val){
40617         if(val && val instanceof Date){
40618             return true;
40619         }else if(typeof val == 'object' || typeof val == 'function'){
40620             return false;
40621         }
40622         return true;
40623     },
40624
40625     setValue : function(prop, value){
40626         this.source[prop] = value;
40627         this.store.getById(prop).set('value', value);
40628     },
40629
40630     getSource : function(){
40631         return this.source;
40632     }
40633 });
40634
40635 Roo.grid.PropertyColumnModel = function(grid, store){
40636     this.grid = grid;
40637     var g = Roo.grid;
40638     g.PropertyColumnModel.superclass.constructor.call(this, [
40639         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40640         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40641     ]);
40642     this.store = store;
40643     this.bselect = Roo.DomHelper.append(document.body, {
40644         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40645             {tag: 'option', value: 'true', html: 'true'},
40646             {tag: 'option', value: 'false', html: 'false'}
40647         ]
40648     });
40649     Roo.id(this.bselect);
40650     var f = Roo.form;
40651     this.editors = {
40652         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40653         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40654         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40655         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40656         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40657     };
40658     this.renderCellDelegate = this.renderCell.createDelegate(this);
40659     this.renderPropDelegate = this.renderProp.createDelegate(this);
40660 };
40661
40662 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40663     
40664     
40665     nameText : 'Name',
40666     valueText : 'Value',
40667     
40668     dateFormat : 'm/j/Y',
40669     
40670     
40671     renderDate : function(dateVal){
40672         return dateVal.dateFormat(this.dateFormat);
40673     },
40674
40675     renderBool : function(bVal){
40676         return bVal ? 'true' : 'false';
40677     },
40678
40679     isCellEditable : function(colIndex, rowIndex){
40680         return colIndex == 1;
40681     },
40682
40683     getRenderer : function(col){
40684         return col == 1 ?
40685             this.renderCellDelegate : this.renderPropDelegate;
40686     },
40687
40688     renderProp : function(v){
40689         return this.getPropertyName(v);
40690     },
40691
40692     renderCell : function(val){
40693         var rv = val;
40694         if(val instanceof Date){
40695             rv = this.renderDate(val);
40696         }else if(typeof val == 'boolean'){
40697             rv = this.renderBool(val);
40698         }
40699         return Roo.util.Format.htmlEncode(rv);
40700     },
40701
40702     getPropertyName : function(name){
40703         var pn = this.grid.propertyNames;
40704         return pn && pn[name] ? pn[name] : name;
40705     },
40706
40707     getCellEditor : function(colIndex, rowIndex){
40708         var p = this.store.getProperty(rowIndex);
40709         var n = p.data['name'], val = p.data['value'];
40710         
40711         if(typeof(this.grid.customEditors[n]) == 'string'){
40712             return this.editors[this.grid.customEditors[n]];
40713         }
40714         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40715             return this.grid.customEditors[n];
40716         }
40717         if(val instanceof Date){
40718             return this.editors['date'];
40719         }else if(typeof val == 'number'){
40720             return this.editors['number'];
40721         }else if(typeof val == 'boolean'){
40722             return this.editors['boolean'];
40723         }else{
40724             return this.editors['string'];
40725         }
40726     }
40727 });
40728
40729 /**
40730  * @class Roo.grid.PropertyGrid
40731  * @extends Roo.grid.EditorGrid
40732  * This class represents the  interface of a component based property grid control.
40733  * <br><br>Usage:<pre><code>
40734  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40735       
40736  });
40737  // set any options
40738  grid.render();
40739  * </code></pre>
40740   
40741  * @constructor
40742  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40743  * The container MUST have some type of size defined for the grid to fill. The container will be
40744  * automatically set to position relative if it isn't already.
40745  * @param {Object} config A config object that sets properties on this grid.
40746  */
40747 Roo.grid.PropertyGrid = function(container, config){
40748     config = config || {};
40749     var store = new Roo.grid.PropertyStore(this);
40750     this.store = store;
40751     var cm = new Roo.grid.PropertyColumnModel(this, store);
40752     store.store.sort('name', 'ASC');
40753     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40754         ds: store.store,
40755         cm: cm,
40756         enableColLock:false,
40757         enableColumnMove:false,
40758         stripeRows:false,
40759         trackMouseOver: false,
40760         clicksToEdit:1
40761     }, config));
40762     this.getGridEl().addClass('x-props-grid');
40763     this.lastEditRow = null;
40764     this.on('columnresize', this.onColumnResize, this);
40765     this.addEvents({
40766          /**
40767              * @event beforepropertychange
40768              * Fires before a property changes (return false to stop?)
40769              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40770              * @param {String} id Record Id
40771              * @param {String} newval New Value
40772          * @param {String} oldval Old Value
40773              */
40774         "beforepropertychange": true,
40775         /**
40776              * @event propertychange
40777              * Fires after a property changes
40778              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40779              * @param {String} id Record Id
40780              * @param {String} newval New Value
40781          * @param {String} oldval Old Value
40782              */
40783         "propertychange": true
40784     });
40785     this.customEditors = this.customEditors || {};
40786 };
40787 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40788     
40789      /**
40790      * @cfg {Object} customEditors map of colnames=> custom editors.
40791      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40792      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40793      * false disables editing of the field.
40794          */
40795     
40796       /**
40797      * @cfg {Object} propertyNames map of property Names to their displayed value
40798          */
40799     
40800     render : function(){
40801         Roo.grid.PropertyGrid.superclass.render.call(this);
40802         this.autoSize.defer(100, this);
40803     },
40804
40805     autoSize : function(){
40806         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40807         if(this.view){
40808             this.view.fitColumns();
40809         }
40810     },
40811
40812     onColumnResize : function(){
40813         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40814         this.autoSize();
40815     },
40816     /**
40817      * Sets the data for the Grid
40818      * accepts a Key => Value object of all the elements avaiable.
40819      * @param {Object} data  to appear in grid.
40820      */
40821     setSource : function(source){
40822         this.store.setSource(source);
40823         //this.autoSize();
40824     },
40825     /**
40826      * Gets all the data from the grid.
40827      * @return {Object} data  data stored in grid
40828      */
40829     getSource : function(){
40830         return this.store.getSource();
40831     }
40832 });/*
40833   
40834  * Licence LGPL
40835  
40836  */
40837  
40838 /**
40839  * @class Roo.grid.Calendar
40840  * @extends Roo.util.Grid
40841  * This class extends the Grid to provide a calendar widget
40842  * <br><br>Usage:<pre><code>
40843  var grid = new Roo.grid.Calendar("my-container-id", {
40844      ds: myDataStore,
40845      cm: myColModel,
40846      selModel: mySelectionModel,
40847      autoSizeColumns: true,
40848      monitorWindowResize: false,
40849      trackMouseOver: true
40850      eventstore : real data store..
40851  });
40852  // set any options
40853  grid.render();
40854   
40855   * @constructor
40856  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40857  * The container MUST have some type of size defined for the grid to fill. The container will be
40858  * automatically set to position relative if it isn't already.
40859  * @param {Object} config A config object that sets properties on this grid.
40860  */
40861 Roo.grid.Calendar = function(container, config){
40862         // initialize the container
40863         this.container = Roo.get(container);
40864         this.container.update("");
40865         this.container.setStyle("overflow", "hidden");
40866     this.container.addClass('x-grid-container');
40867
40868     this.id = this.container.id;
40869
40870     Roo.apply(this, config);
40871     // check and correct shorthanded configs
40872     
40873     var rows = [];
40874     var d =1;
40875     for (var r = 0;r < 6;r++) {
40876         
40877         rows[r]=[];
40878         for (var c =0;c < 7;c++) {
40879             rows[r][c]= '';
40880         }
40881     }
40882     if (this.eventStore) {
40883         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40884         this.eventStore.on('load',this.onLoad, this);
40885         this.eventStore.on('beforeload',this.clearEvents, this);
40886          
40887     }
40888     
40889     this.dataSource = new Roo.data.Store({
40890             proxy: new Roo.data.MemoryProxy(rows),
40891             reader: new Roo.data.ArrayReader({}, [
40892                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40893     });
40894
40895     this.dataSource.load();
40896     this.ds = this.dataSource;
40897     this.ds.xmodule = this.xmodule || false;
40898     
40899     
40900     var cellRender = function(v,x,r)
40901     {
40902         return String.format(
40903             '<div class="fc-day  fc-widget-content"><div>' +
40904                 '<div class="fc-event-container"></div>' +
40905                 '<div class="fc-day-number">{0}</div>'+
40906                 
40907                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40908             '</div></div>', v);
40909     
40910     }
40911     
40912     
40913     this.colModel = new Roo.grid.ColumnModel( [
40914         {
40915             xtype: 'ColumnModel',
40916             xns: Roo.grid,
40917             dataIndex : 'weekday0',
40918             header : 'Sunday',
40919             renderer : cellRender
40920         },
40921         {
40922             xtype: 'ColumnModel',
40923             xns: Roo.grid,
40924             dataIndex : 'weekday1',
40925             header : 'Monday',
40926             renderer : cellRender
40927         },
40928         {
40929             xtype: 'ColumnModel',
40930             xns: Roo.grid,
40931             dataIndex : 'weekday2',
40932             header : 'Tuesday',
40933             renderer : cellRender
40934         },
40935         {
40936             xtype: 'ColumnModel',
40937             xns: Roo.grid,
40938             dataIndex : 'weekday3',
40939             header : 'Wednesday',
40940             renderer : cellRender
40941         },
40942         {
40943             xtype: 'ColumnModel',
40944             xns: Roo.grid,
40945             dataIndex : 'weekday4',
40946             header : 'Thursday',
40947             renderer : cellRender
40948         },
40949         {
40950             xtype: 'ColumnModel',
40951             xns: Roo.grid,
40952             dataIndex : 'weekday5',
40953             header : 'Friday',
40954             renderer : cellRender
40955         },
40956         {
40957             xtype: 'ColumnModel',
40958             xns: Roo.grid,
40959             dataIndex : 'weekday6',
40960             header : 'Saturday',
40961             renderer : cellRender
40962         }
40963     ]);
40964     this.cm = this.colModel;
40965     this.cm.xmodule = this.xmodule || false;
40966  
40967         
40968           
40969     //this.selModel = new Roo.grid.CellSelectionModel();
40970     //this.sm = this.selModel;
40971     //this.selModel.init(this);
40972     
40973     
40974     if(this.width){
40975         this.container.setWidth(this.width);
40976     }
40977
40978     if(this.height){
40979         this.container.setHeight(this.height);
40980     }
40981     /** @private */
40982         this.addEvents({
40983         // raw events
40984         /**
40985          * @event click
40986          * The raw click event for the entire grid.
40987          * @param {Roo.EventObject} e
40988          */
40989         "click" : true,
40990         /**
40991          * @event dblclick
40992          * The raw dblclick event for the entire grid.
40993          * @param {Roo.EventObject} e
40994          */
40995         "dblclick" : true,
40996         /**
40997          * @event contextmenu
40998          * The raw contextmenu event for the entire grid.
40999          * @param {Roo.EventObject} e
41000          */
41001         "contextmenu" : true,
41002         /**
41003          * @event mousedown
41004          * The raw mousedown event for the entire grid.
41005          * @param {Roo.EventObject} e
41006          */
41007         "mousedown" : true,
41008         /**
41009          * @event mouseup
41010          * The raw mouseup event for the entire grid.
41011          * @param {Roo.EventObject} e
41012          */
41013         "mouseup" : true,
41014         /**
41015          * @event mouseover
41016          * The raw mouseover event for the entire grid.
41017          * @param {Roo.EventObject} e
41018          */
41019         "mouseover" : true,
41020         /**
41021          * @event mouseout
41022          * The raw mouseout event for the entire grid.
41023          * @param {Roo.EventObject} e
41024          */
41025         "mouseout" : true,
41026         /**
41027          * @event keypress
41028          * The raw keypress event for the entire grid.
41029          * @param {Roo.EventObject} e
41030          */
41031         "keypress" : true,
41032         /**
41033          * @event keydown
41034          * The raw keydown event for the entire grid.
41035          * @param {Roo.EventObject} e
41036          */
41037         "keydown" : true,
41038
41039         // custom events
41040
41041         /**
41042          * @event cellclick
41043          * Fires when a cell is clicked
41044          * @param {Grid} this
41045          * @param {Number} rowIndex
41046          * @param {Number} columnIndex
41047          * @param {Roo.EventObject} e
41048          */
41049         "cellclick" : true,
41050         /**
41051          * @event celldblclick
41052          * Fires when a cell is double clicked
41053          * @param {Grid} this
41054          * @param {Number} rowIndex
41055          * @param {Number} columnIndex
41056          * @param {Roo.EventObject} e
41057          */
41058         "celldblclick" : true,
41059         /**
41060          * @event rowclick
41061          * Fires when a row is clicked
41062          * @param {Grid} this
41063          * @param {Number} rowIndex
41064          * @param {Roo.EventObject} e
41065          */
41066         "rowclick" : true,
41067         /**
41068          * @event rowdblclick
41069          * Fires when a row is double clicked
41070          * @param {Grid} this
41071          * @param {Number} rowIndex
41072          * @param {Roo.EventObject} e
41073          */
41074         "rowdblclick" : true,
41075         /**
41076          * @event headerclick
41077          * Fires when a header is clicked
41078          * @param {Grid} this
41079          * @param {Number} columnIndex
41080          * @param {Roo.EventObject} e
41081          */
41082         "headerclick" : true,
41083         /**
41084          * @event headerdblclick
41085          * Fires when a header cell is double clicked
41086          * @param {Grid} this
41087          * @param {Number} columnIndex
41088          * @param {Roo.EventObject} e
41089          */
41090         "headerdblclick" : true,
41091         /**
41092          * @event rowcontextmenu
41093          * Fires when a row is right clicked
41094          * @param {Grid} this
41095          * @param {Number} rowIndex
41096          * @param {Roo.EventObject} e
41097          */
41098         "rowcontextmenu" : true,
41099         /**
41100          * @event cellcontextmenu
41101          * Fires when a cell is right clicked
41102          * @param {Grid} this
41103          * @param {Number} rowIndex
41104          * @param {Number} cellIndex
41105          * @param {Roo.EventObject} e
41106          */
41107          "cellcontextmenu" : true,
41108         /**
41109          * @event headercontextmenu
41110          * Fires when a header is right clicked
41111          * @param {Grid} this
41112          * @param {Number} columnIndex
41113          * @param {Roo.EventObject} e
41114          */
41115         "headercontextmenu" : true,
41116         /**
41117          * @event bodyscroll
41118          * Fires when the body element is scrolled
41119          * @param {Number} scrollLeft
41120          * @param {Number} scrollTop
41121          */
41122         "bodyscroll" : true,
41123         /**
41124          * @event columnresize
41125          * Fires when the user resizes a column
41126          * @param {Number} columnIndex
41127          * @param {Number} newSize
41128          */
41129         "columnresize" : true,
41130         /**
41131          * @event columnmove
41132          * Fires when the user moves a column
41133          * @param {Number} oldIndex
41134          * @param {Number} newIndex
41135          */
41136         "columnmove" : true,
41137         /**
41138          * @event startdrag
41139          * Fires when row(s) start being dragged
41140          * @param {Grid} this
41141          * @param {Roo.GridDD} dd The drag drop object
41142          * @param {event} e The raw browser event
41143          */
41144         "startdrag" : true,
41145         /**
41146          * @event enddrag
41147          * Fires when a drag operation is complete
41148          * @param {Grid} this
41149          * @param {Roo.GridDD} dd The drag drop object
41150          * @param {event} e The raw browser event
41151          */
41152         "enddrag" : true,
41153         /**
41154          * @event dragdrop
41155          * Fires when dragged row(s) are dropped on a valid DD target
41156          * @param {Grid} this
41157          * @param {Roo.GridDD} dd The drag drop object
41158          * @param {String} targetId The target drag drop object
41159          * @param {event} e The raw browser event
41160          */
41161         "dragdrop" : true,
41162         /**
41163          * @event dragover
41164          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41165          * @param {Grid} this
41166          * @param {Roo.GridDD} dd The drag drop object
41167          * @param {String} targetId The target drag drop object
41168          * @param {event} e The raw browser event
41169          */
41170         "dragover" : true,
41171         /**
41172          * @event dragenter
41173          *  Fires when the dragged row(s) first cross another DD target while being dragged
41174          * @param {Grid} this
41175          * @param {Roo.GridDD} dd The drag drop object
41176          * @param {String} targetId The target drag drop object
41177          * @param {event} e The raw browser event
41178          */
41179         "dragenter" : true,
41180         /**
41181          * @event dragout
41182          * Fires when the dragged row(s) leave another DD target while being dragged
41183          * @param {Grid} this
41184          * @param {Roo.GridDD} dd The drag drop object
41185          * @param {String} targetId The target drag drop object
41186          * @param {event} e The raw browser event
41187          */
41188         "dragout" : true,
41189         /**
41190          * @event rowclass
41191          * Fires when a row is rendered, so you can change add a style to it.
41192          * @param {GridView} gridview   The grid view
41193          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41194          */
41195         'rowclass' : true,
41196
41197         /**
41198          * @event render
41199          * Fires when the grid is rendered
41200          * @param {Grid} grid
41201          */
41202         'render' : true,
41203             /**
41204              * @event select
41205              * Fires when a date is selected
41206              * @param {DatePicker} this
41207              * @param {Date} date The selected date
41208              */
41209         'select': true,
41210         /**
41211              * @event monthchange
41212              * Fires when the displayed month changes 
41213              * @param {DatePicker} this
41214              * @param {Date} date The selected month
41215              */
41216         'monthchange': true,
41217         /**
41218              * @event evententer
41219              * Fires when mouse over an event
41220              * @param {Calendar} this
41221              * @param {event} Event
41222              */
41223         'evententer': true,
41224         /**
41225              * @event eventleave
41226              * Fires when the mouse leaves an
41227              * @param {Calendar} this
41228              * @param {event}
41229              */
41230         'eventleave': true,
41231         /**
41232              * @event eventclick
41233              * Fires when the mouse click an
41234              * @param {Calendar} this
41235              * @param {event}
41236              */
41237         'eventclick': true,
41238         /**
41239              * @event eventrender
41240              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41241              * @param {Calendar} this
41242              * @param {data} data to be modified
41243              */
41244         'eventrender': true
41245         
41246     });
41247
41248     Roo.grid.Grid.superclass.constructor.call(this);
41249     this.on('render', function() {
41250         this.view.el.addClass('x-grid-cal'); 
41251         
41252         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41253
41254     },this);
41255     
41256     if (!Roo.grid.Calendar.style) {
41257         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41258             
41259             
41260             '.x-grid-cal .x-grid-col' :  {
41261                 height: 'auto !important',
41262                 'vertical-align': 'top'
41263             },
41264             '.x-grid-cal  .fc-event-hori' : {
41265                 height: '14px'
41266             }
41267              
41268             
41269         }, Roo.id());
41270     }
41271
41272     
41273     
41274 };
41275 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41276     /**
41277      * @cfg {Store} eventStore The store that loads events.
41278      */
41279     eventStore : 25,
41280
41281      
41282     activeDate : false,
41283     startDay : 0,
41284     autoWidth : true,
41285     monitorWindowResize : false,
41286
41287     
41288     resizeColumns : function() {
41289         var col = (this.view.el.getWidth() / 7) - 3;
41290         // loop through cols, and setWidth
41291         for(var i =0 ; i < 7 ; i++){
41292             this.cm.setColumnWidth(i, col);
41293         }
41294     },
41295      setDate :function(date) {
41296         
41297         Roo.log('setDate?');
41298         
41299         this.resizeColumns();
41300         var vd = this.activeDate;
41301         this.activeDate = date;
41302 //        if(vd && this.el){
41303 //            var t = date.getTime();
41304 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41305 //                Roo.log('using add remove');
41306 //                
41307 //                this.fireEvent('monthchange', this, date);
41308 //                
41309 //                this.cells.removeClass("fc-state-highlight");
41310 //                this.cells.each(function(c){
41311 //                   if(c.dateValue == t){
41312 //                       c.addClass("fc-state-highlight");
41313 //                       setTimeout(function(){
41314 //                            try{c.dom.firstChild.focus();}catch(e){}
41315 //                       }, 50);
41316 //                       return false;
41317 //                   }
41318 //                   return true;
41319 //                });
41320 //                return;
41321 //            }
41322 //        }
41323         
41324         var days = date.getDaysInMonth();
41325         
41326         var firstOfMonth = date.getFirstDateOfMonth();
41327         var startingPos = firstOfMonth.getDay()-this.startDay;
41328         
41329         if(startingPos < this.startDay){
41330             startingPos += 7;
41331         }
41332         
41333         var pm = date.add(Date.MONTH, -1);
41334         var prevStart = pm.getDaysInMonth()-startingPos;
41335 //        
41336         
41337         
41338         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41339         
41340         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41341         //this.cells.addClassOnOver('fc-state-hover');
41342         
41343         var cells = this.cells.elements;
41344         var textEls = this.textNodes;
41345         
41346         //Roo.each(cells, function(cell){
41347         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41348         //});
41349         
41350         days += startingPos;
41351
41352         // convert everything to numbers so it's fast
41353         var day = 86400000;
41354         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41355         //Roo.log(d);
41356         //Roo.log(pm);
41357         //Roo.log(prevStart);
41358         
41359         var today = new Date().clearTime().getTime();
41360         var sel = date.clearTime().getTime();
41361         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41362         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41363         var ddMatch = this.disabledDatesRE;
41364         var ddText = this.disabledDatesText;
41365         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41366         var ddaysText = this.disabledDaysText;
41367         var format = this.format;
41368         
41369         var setCellClass = function(cal, cell){
41370             
41371             //Roo.log('set Cell Class');
41372             cell.title = "";
41373             var t = d.getTime();
41374             
41375             //Roo.log(d);
41376             
41377             
41378             cell.dateValue = t;
41379             if(t == today){
41380                 cell.className += " fc-today";
41381                 cell.className += " fc-state-highlight";
41382                 cell.title = cal.todayText;
41383             }
41384             if(t == sel){
41385                 // disable highlight in other month..
41386                 cell.className += " fc-state-highlight";
41387                 
41388             }
41389             // disabling
41390             if(t < min) {
41391                 //cell.className = " fc-state-disabled";
41392                 cell.title = cal.minText;
41393                 return;
41394             }
41395             if(t > max) {
41396                 //cell.className = " fc-state-disabled";
41397                 cell.title = cal.maxText;
41398                 return;
41399             }
41400             if(ddays){
41401                 if(ddays.indexOf(d.getDay()) != -1){
41402                     // cell.title = ddaysText;
41403                    // cell.className = " fc-state-disabled";
41404                 }
41405             }
41406             if(ddMatch && format){
41407                 var fvalue = d.dateFormat(format);
41408                 if(ddMatch.test(fvalue)){
41409                     cell.title = ddText.replace("%0", fvalue);
41410                    cell.className = " fc-state-disabled";
41411                 }
41412             }
41413             
41414             if (!cell.initialClassName) {
41415                 cell.initialClassName = cell.dom.className;
41416             }
41417             
41418             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41419         };
41420
41421         var i = 0;
41422         
41423         for(; i < startingPos; i++) {
41424             cells[i].dayName =  (++prevStart);
41425             Roo.log(textEls[i]);
41426             d.setDate(d.getDate()+1);
41427             
41428             //cells[i].className = "fc-past fc-other-month";
41429             setCellClass(this, cells[i]);
41430         }
41431         
41432         var intDay = 0;
41433         
41434         for(; i < days; i++){
41435             intDay = i - startingPos + 1;
41436             cells[i].dayName =  (intDay);
41437             d.setDate(d.getDate()+1);
41438             
41439             cells[i].className = ''; // "x-date-active";
41440             setCellClass(this, cells[i]);
41441         }
41442         var extraDays = 0;
41443         
41444         for(; i < 42; i++) {
41445             //textEls[i].innerHTML = (++extraDays);
41446             
41447             d.setDate(d.getDate()+1);
41448             cells[i].dayName = (++extraDays);
41449             cells[i].className = "fc-future fc-other-month";
41450             setCellClass(this, cells[i]);
41451         }
41452         
41453         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41454         
41455         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41456         
41457         // this will cause all the cells to mis
41458         var rows= [];
41459         var i =0;
41460         for (var r = 0;r < 6;r++) {
41461             for (var c =0;c < 7;c++) {
41462                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41463             }    
41464         }
41465         
41466         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41467         for(i=0;i<cells.length;i++) {
41468             
41469             this.cells.elements[i].dayName = cells[i].dayName ;
41470             this.cells.elements[i].className = cells[i].className;
41471             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41472             this.cells.elements[i].title = cells[i].title ;
41473             this.cells.elements[i].dateValue = cells[i].dateValue ;
41474         }
41475         
41476         
41477         
41478         
41479         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41480         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41481         
41482         ////if(totalRows != 6){
41483             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41484            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41485        // }
41486         
41487         this.fireEvent('monthchange', this, date);
41488         
41489         
41490     },
41491  /**
41492      * Returns the grid's SelectionModel.
41493      * @return {SelectionModel}
41494      */
41495     getSelectionModel : function(){
41496         if(!this.selModel){
41497             this.selModel = new Roo.grid.CellSelectionModel();
41498         }
41499         return this.selModel;
41500     },
41501
41502     load: function() {
41503         this.eventStore.load()
41504         
41505         
41506         
41507     },
41508     
41509     findCell : function(dt) {
41510         dt = dt.clearTime().getTime();
41511         var ret = false;
41512         this.cells.each(function(c){
41513             //Roo.log("check " +c.dateValue + '?=' + dt);
41514             if(c.dateValue == dt){
41515                 ret = c;
41516                 return false;
41517             }
41518             return true;
41519         });
41520         
41521         return ret;
41522     },
41523     
41524     findCells : function(rec) {
41525         var s = rec.data.start_dt.clone().clearTime().getTime();
41526        // Roo.log(s);
41527         var e= rec.data.end_dt.clone().clearTime().getTime();
41528        // Roo.log(e);
41529         var ret = [];
41530         this.cells.each(function(c){
41531              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41532             
41533             if(c.dateValue > e){
41534                 return ;
41535             }
41536             if(c.dateValue < s){
41537                 return ;
41538             }
41539             ret.push(c);
41540         });
41541         
41542         return ret;    
41543     },
41544     
41545     findBestRow: function(cells)
41546     {
41547         var ret = 0;
41548         
41549         for (var i =0 ; i < cells.length;i++) {
41550             ret  = Math.max(cells[i].rows || 0,ret);
41551         }
41552         return ret;
41553         
41554     },
41555     
41556     
41557     addItem : function(rec)
41558     {
41559         // look for vertical location slot in
41560         var cells = this.findCells(rec);
41561         
41562         rec.row = this.findBestRow(cells);
41563         
41564         // work out the location.
41565         
41566         var crow = false;
41567         var rows = [];
41568         for(var i =0; i < cells.length; i++) {
41569             if (!crow) {
41570                 crow = {
41571                     start : cells[i],
41572                     end :  cells[i]
41573                 };
41574                 continue;
41575             }
41576             if (crow.start.getY() == cells[i].getY()) {
41577                 // on same row.
41578                 crow.end = cells[i];
41579                 continue;
41580             }
41581             // different row.
41582             rows.push(crow);
41583             crow = {
41584                 start: cells[i],
41585                 end : cells[i]
41586             };
41587             
41588         }
41589         
41590         rows.push(crow);
41591         rec.els = [];
41592         rec.rows = rows;
41593         rec.cells = cells;
41594         for (var i = 0; i < cells.length;i++) {
41595             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41596             
41597         }
41598         
41599         
41600     },
41601     
41602     clearEvents: function() {
41603         
41604         if (!this.eventStore.getCount()) {
41605             return;
41606         }
41607         // reset number of rows in cells.
41608         Roo.each(this.cells.elements, function(c){
41609             c.rows = 0;
41610         });
41611         
41612         this.eventStore.each(function(e) {
41613             this.clearEvent(e);
41614         },this);
41615         
41616     },
41617     
41618     clearEvent : function(ev)
41619     {
41620         if (ev.els) {
41621             Roo.each(ev.els, function(el) {
41622                 el.un('mouseenter' ,this.onEventEnter, this);
41623                 el.un('mouseleave' ,this.onEventLeave, this);
41624                 el.remove();
41625             },this);
41626             ev.els = [];
41627         }
41628     },
41629     
41630     
41631     renderEvent : function(ev,ctr) {
41632         if (!ctr) {
41633              ctr = this.view.el.select('.fc-event-container',true).first();
41634         }
41635         
41636          
41637         this.clearEvent(ev);
41638             //code
41639        
41640         
41641         
41642         ev.els = [];
41643         var cells = ev.cells;
41644         var rows = ev.rows;
41645         this.fireEvent('eventrender', this, ev);
41646         
41647         for(var i =0; i < rows.length; i++) {
41648             
41649             cls = '';
41650             if (i == 0) {
41651                 cls += ' fc-event-start';
41652             }
41653             if ((i+1) == rows.length) {
41654                 cls += ' fc-event-end';
41655             }
41656             
41657             //Roo.log(ev.data);
41658             // how many rows should it span..
41659             var cg = this.eventTmpl.append(ctr,Roo.apply({
41660                 fccls : cls
41661                 
41662             }, ev.data) , true);
41663             
41664             
41665             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41666             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41667             cg.on('click', this.onEventClick, this, ev);
41668             
41669             ev.els.push(cg);
41670             
41671             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41672             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41673             //Roo.log(cg);
41674              
41675             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41676             cg.setWidth(ebox.right - sbox.x -2);
41677         }
41678     },
41679     
41680     renderEvents: function()
41681     {   
41682         // first make sure there is enough space..
41683         
41684         if (!this.eventTmpl) {
41685             this.eventTmpl = new Roo.Template(
41686                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41687                     '<div class="fc-event-inner">' +
41688                         '<span class="fc-event-time">{time}</span>' +
41689                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41690                     '</div>' +
41691                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41692                 '</div>'
41693             );
41694                 
41695         }
41696                
41697         
41698         
41699         this.cells.each(function(c) {
41700             //Roo.log(c.select('.fc-day-content div',true).first());
41701             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41702         });
41703         
41704         var ctr = this.view.el.select('.fc-event-container',true).first();
41705         
41706         var cls;
41707         this.eventStore.each(function(ev){
41708             
41709             this.renderEvent(ev);
41710              
41711              
41712         }, this);
41713         this.view.layout();
41714         
41715     },
41716     
41717     onEventEnter: function (e, el,event,d) {
41718         this.fireEvent('evententer', this, el, event);
41719     },
41720     
41721     onEventLeave: function (e, el,event,d) {
41722         this.fireEvent('eventleave', this, el, event);
41723     },
41724     
41725     onEventClick: function (e, el,event,d) {
41726         this.fireEvent('eventclick', this, el, event);
41727     },
41728     
41729     onMonthChange: function () {
41730         this.store.load();
41731     },
41732     
41733     onLoad: function () {
41734         
41735         //Roo.log('calendar onload');
41736 //         
41737         if(this.eventStore.getCount() > 0){
41738             
41739            
41740             
41741             this.eventStore.each(function(d){
41742                 
41743                 
41744                 // FIXME..
41745                 var add =   d.data;
41746                 if (typeof(add.end_dt) == 'undefined')  {
41747                     Roo.log("Missing End time in calendar data: ");
41748                     Roo.log(d);
41749                     return;
41750                 }
41751                 if (typeof(add.start_dt) == 'undefined')  {
41752                     Roo.log("Missing Start time in calendar data: ");
41753                     Roo.log(d);
41754                     return;
41755                 }
41756                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41757                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41758                 add.id = add.id || d.id;
41759                 add.title = add.title || '??';
41760                 
41761                 this.addItem(d);
41762                 
41763              
41764             },this);
41765         }
41766         
41767         this.renderEvents();
41768     }
41769     
41770
41771 });
41772 /*
41773  grid : {
41774                 xtype: 'Grid',
41775                 xns: Roo.grid,
41776                 listeners : {
41777                     render : function ()
41778                     {
41779                         _this.grid = this;
41780                         
41781                         if (!this.view.el.hasClass('course-timesheet')) {
41782                             this.view.el.addClass('course-timesheet');
41783                         }
41784                         if (this.tsStyle) {
41785                             this.ds.load({});
41786                             return; 
41787                         }
41788                         Roo.log('width');
41789                         Roo.log(_this.grid.view.el.getWidth());
41790                         
41791                         
41792                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41793                             '.course-timesheet .x-grid-row' : {
41794                                 height: '80px'
41795                             },
41796                             '.x-grid-row td' : {
41797                                 'vertical-align' : 0
41798                             },
41799                             '.course-edit-link' : {
41800                                 'color' : 'blue',
41801                                 'text-overflow' : 'ellipsis',
41802                                 'overflow' : 'hidden',
41803                                 'white-space' : 'nowrap',
41804                                 'cursor' : 'pointer'
41805                             },
41806                             '.sub-link' : {
41807                                 'color' : 'green'
41808                             },
41809                             '.de-act-sup-link' : {
41810                                 'color' : 'purple',
41811                                 'text-decoration' : 'line-through'
41812                             },
41813                             '.de-act-link' : {
41814                                 'color' : 'red',
41815                                 'text-decoration' : 'line-through'
41816                             },
41817                             '.course-timesheet .course-highlight' : {
41818                                 'border-top-style': 'dashed !important',
41819                                 'border-bottom-bottom': 'dashed !important'
41820                             },
41821                             '.course-timesheet .course-item' : {
41822                                 'font-family'   : 'tahoma, arial, helvetica',
41823                                 'font-size'     : '11px',
41824                                 'overflow'      : 'hidden',
41825                                 'padding-left'  : '10px',
41826                                 'padding-right' : '10px',
41827                                 'padding-top' : '10px' 
41828                             }
41829                             
41830                         }, Roo.id());
41831                                 this.ds.load({});
41832                     }
41833                 },
41834                 autoWidth : true,
41835                 monitorWindowResize : false,
41836                 cellrenderer : function(v,x,r)
41837                 {
41838                     return v;
41839                 },
41840                 sm : {
41841                     xtype: 'CellSelectionModel',
41842                     xns: Roo.grid
41843                 },
41844                 dataSource : {
41845                     xtype: 'Store',
41846                     xns: Roo.data,
41847                     listeners : {
41848                         beforeload : function (_self, options)
41849                         {
41850                             options.params = options.params || {};
41851                             options.params._month = _this.monthField.getValue();
41852                             options.params.limit = 9999;
41853                             options.params['sort'] = 'when_dt';    
41854                             options.params['dir'] = 'ASC';    
41855                             this.proxy.loadResponse = this.loadResponse;
41856                             Roo.log("load?");
41857                             //this.addColumns();
41858                         },
41859                         load : function (_self, records, options)
41860                         {
41861                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41862                                 // if you click on the translation.. you can edit it...
41863                                 var el = Roo.get(this);
41864                                 var id = el.dom.getAttribute('data-id');
41865                                 var d = el.dom.getAttribute('data-date');
41866                                 var t = el.dom.getAttribute('data-time');
41867                                 //var id = this.child('span').dom.textContent;
41868                                 
41869                                 //Roo.log(this);
41870                                 Pman.Dialog.CourseCalendar.show({
41871                                     id : id,
41872                                     when_d : d,
41873                                     when_t : t,
41874                                     productitem_active : id ? 1 : 0
41875                                 }, function() {
41876                                     _this.grid.ds.load({});
41877                                 });
41878                            
41879                            });
41880                            
41881                            _this.panel.fireEvent('resize', [ '', '' ]);
41882                         }
41883                     },
41884                     loadResponse : function(o, success, response){
41885                             // this is overridden on before load..
41886                             
41887                             Roo.log("our code?");       
41888                             //Roo.log(success);
41889                             //Roo.log(response)
41890                             delete this.activeRequest;
41891                             if(!success){
41892                                 this.fireEvent("loadexception", this, o, response);
41893                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41894                                 return;
41895                             }
41896                             var result;
41897                             try {
41898                                 result = o.reader.read(response);
41899                             }catch(e){
41900                                 Roo.log("load exception?");
41901                                 this.fireEvent("loadexception", this, o, response, e);
41902                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41903                                 return;
41904                             }
41905                             Roo.log("ready...");        
41906                             // loop through result.records;
41907                             // and set this.tdate[date] = [] << array of records..
41908                             _this.tdata  = {};
41909                             Roo.each(result.records, function(r){
41910                                 //Roo.log(r.data);
41911                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41912                                     _this.tdata[r.data.when_dt.format('j')] = [];
41913                                 }
41914                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41915                             });
41916                             
41917                             //Roo.log(_this.tdata);
41918                             
41919                             result.records = [];
41920                             result.totalRecords = 6;
41921                     
41922                             // let's generate some duumy records for the rows.
41923                             //var st = _this.dateField.getValue();
41924                             
41925                             // work out monday..
41926                             //st = st.add(Date.DAY, -1 * st.format('w'));
41927                             
41928                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41929                             
41930                             var firstOfMonth = date.getFirstDayOfMonth();
41931                             var days = date.getDaysInMonth();
41932                             var d = 1;
41933                             var firstAdded = false;
41934                             for (var i = 0; i < result.totalRecords ; i++) {
41935                                 //var d= st.add(Date.DAY, i);
41936                                 var row = {};
41937                                 var added = 0;
41938                                 for(var w = 0 ; w < 7 ; w++){
41939                                     if(!firstAdded && firstOfMonth != w){
41940                                         continue;
41941                                     }
41942                                     if(d > days){
41943                                         continue;
41944                                     }
41945                                     firstAdded = true;
41946                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41947                                     row['weekday'+w] = String.format(
41948                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41949                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41950                                                     d,
41951                                                     date.format('Y-m-')+dd
41952                                                 );
41953                                     added++;
41954                                     if(typeof(_this.tdata[d]) != 'undefined'){
41955                                         Roo.each(_this.tdata[d], function(r){
41956                                             var is_sub = '';
41957                                             var deactive = '';
41958                                             var id = r.id;
41959                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41960                                             if(r.parent_id*1>0){
41961                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41962                                                 id = r.parent_id;
41963                                             }
41964                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41965                                                 deactive = 'de-act-link';
41966                                             }
41967                                             
41968                                             row['weekday'+w] += String.format(
41969                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41970                                                     id, //0
41971                                                     r.product_id_name, //1
41972                                                     r.when_dt.format('h:ia'), //2
41973                                                     is_sub, //3
41974                                                     deactive, //4
41975                                                     desc // 5
41976                                             );
41977                                         });
41978                                     }
41979                                     d++;
41980                                 }
41981                                 
41982                                 // only do this if something added..
41983                                 if(added > 0){ 
41984                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41985                                 }
41986                                 
41987                                 
41988                                 // push it twice. (second one with an hour..
41989                                 
41990                             }
41991                             //Roo.log(result);
41992                             this.fireEvent("load", this, o, o.request.arg);
41993                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41994                         },
41995                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41996                     proxy : {
41997                         xtype: 'HttpProxy',
41998                         xns: Roo.data,
41999                         method : 'GET',
42000                         url : baseURL + '/Roo/Shop_course.php'
42001                     },
42002                     reader : {
42003                         xtype: 'JsonReader',
42004                         xns: Roo.data,
42005                         id : 'id',
42006                         fields : [
42007                             {
42008                                 'name': 'id',
42009                                 'type': 'int'
42010                             },
42011                             {
42012                                 'name': 'when_dt',
42013                                 'type': 'string'
42014                             },
42015                             {
42016                                 'name': 'end_dt',
42017                                 'type': 'string'
42018                             },
42019                             {
42020                                 'name': 'parent_id',
42021                                 'type': 'int'
42022                             },
42023                             {
42024                                 'name': 'product_id',
42025                                 'type': 'int'
42026                             },
42027                             {
42028                                 'name': 'productitem_id',
42029                                 'type': 'int'
42030                             },
42031                             {
42032                                 'name': 'guid',
42033                                 'type': 'int'
42034                             }
42035                         ]
42036                     }
42037                 },
42038                 toolbar : {
42039                     xtype: 'Toolbar',
42040                     xns: Roo,
42041                     items : [
42042                         {
42043                             xtype: 'Button',
42044                             xns: Roo.Toolbar,
42045                             listeners : {
42046                                 click : function (_self, e)
42047                                 {
42048                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42049                                     sd.setMonth(sd.getMonth()-1);
42050                                     _this.monthField.setValue(sd.format('Y-m-d'));
42051                                     _this.grid.ds.load({});
42052                                 }
42053                             },
42054                             text : "Back"
42055                         },
42056                         {
42057                             xtype: 'Separator',
42058                             xns: Roo.Toolbar
42059                         },
42060                         {
42061                             xtype: 'MonthField',
42062                             xns: Roo.form,
42063                             listeners : {
42064                                 render : function (_self)
42065                                 {
42066                                     _this.monthField = _self;
42067                                    // _this.monthField.set  today
42068                                 },
42069                                 select : function (combo, date)
42070                                 {
42071                                     _this.grid.ds.load({});
42072                                 }
42073                             },
42074                             value : (function() { return new Date(); })()
42075                         },
42076                         {
42077                             xtype: 'Separator',
42078                             xns: Roo.Toolbar
42079                         },
42080                         {
42081                             xtype: 'TextItem',
42082                             xns: Roo.Toolbar,
42083                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42084                         },
42085                         {
42086                             xtype: 'Fill',
42087                             xns: Roo.Toolbar
42088                         },
42089                         {
42090                             xtype: 'Button',
42091                             xns: Roo.Toolbar,
42092                             listeners : {
42093                                 click : function (_self, e)
42094                                 {
42095                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42096                                     sd.setMonth(sd.getMonth()+1);
42097                                     _this.monthField.setValue(sd.format('Y-m-d'));
42098                                     _this.grid.ds.load({});
42099                                 }
42100                             },
42101                             text : "Next"
42102                         }
42103                     ]
42104                 },
42105                  
42106             }
42107         };
42108         
42109         *//*
42110  * Based on:
42111  * Ext JS Library 1.1.1
42112  * Copyright(c) 2006-2007, Ext JS, LLC.
42113  *
42114  * Originally Released Under LGPL - original licence link has changed is not relivant.
42115  *
42116  * Fork - LGPL
42117  * <script type="text/javascript">
42118  */
42119  
42120 /**
42121  * @class Roo.LoadMask
42122  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42123  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42124  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42125  * element's UpdateManager load indicator and will be destroyed after the initial load.
42126  * @constructor
42127  * Create a new LoadMask
42128  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42129  * @param {Object} config The config object
42130  */
42131 Roo.LoadMask = function(el, config){
42132     this.el = Roo.get(el);
42133     Roo.apply(this, config);
42134     if(this.store){
42135         this.store.on('beforeload', this.onBeforeLoad, this);
42136         this.store.on('load', this.onLoad, this);
42137         this.store.on('loadexception', this.onLoadException, this);
42138         this.removeMask = false;
42139     }else{
42140         var um = this.el.getUpdateManager();
42141         um.showLoadIndicator = false; // disable the default indicator
42142         um.on('beforeupdate', this.onBeforeLoad, this);
42143         um.on('update', this.onLoad, this);
42144         um.on('failure', this.onLoad, this);
42145         this.removeMask = true;
42146     }
42147 };
42148
42149 Roo.LoadMask.prototype = {
42150     /**
42151      * @cfg {Boolean} removeMask
42152      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42153      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42154      */
42155     /**
42156      * @cfg {String} msg
42157      * The text to display in a centered loading message box (defaults to 'Loading...')
42158      */
42159     msg : 'Loading...',
42160     /**
42161      * @cfg {String} msgCls
42162      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42163      */
42164     msgCls : 'x-mask-loading',
42165
42166     /**
42167      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42168      * @type Boolean
42169      */
42170     disabled: false,
42171
42172     /**
42173      * Disables the mask to prevent it from being displayed
42174      */
42175     disable : function(){
42176        this.disabled = true;
42177     },
42178
42179     /**
42180      * Enables the mask so that it can be displayed
42181      */
42182     enable : function(){
42183         this.disabled = false;
42184     },
42185     
42186     onLoadException : function()
42187     {
42188         Roo.log(arguments);
42189         
42190         if (typeof(arguments[3]) != 'undefined') {
42191             Roo.MessageBox.alert("Error loading",arguments[3]);
42192         } 
42193         /*
42194         try {
42195             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42196                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42197             }   
42198         } catch(e) {
42199             
42200         }
42201         */
42202     
42203         
42204         
42205         this.el.unmask(this.removeMask);
42206     },
42207     // private
42208     onLoad : function()
42209     {
42210         this.el.unmask(this.removeMask);
42211     },
42212
42213     // private
42214     onBeforeLoad : function(){
42215         if(!this.disabled){
42216             this.el.mask(this.msg, this.msgCls);
42217         }
42218     },
42219
42220     // private
42221     destroy : function(){
42222         if(this.store){
42223             this.store.un('beforeload', this.onBeforeLoad, this);
42224             this.store.un('load', this.onLoad, this);
42225             this.store.un('loadexception', this.onLoadException, this);
42226         }else{
42227             var um = this.el.getUpdateManager();
42228             um.un('beforeupdate', this.onBeforeLoad, this);
42229             um.un('update', this.onLoad, this);
42230             um.un('failure', this.onLoad, this);
42231         }
42232     }
42233 };/*
42234  * Based on:
42235  * Ext JS Library 1.1.1
42236  * Copyright(c) 2006-2007, Ext JS, LLC.
42237  *
42238  * Originally Released Under LGPL - original licence link has changed is not relivant.
42239  *
42240  * Fork - LGPL
42241  * <script type="text/javascript">
42242  */
42243
42244
42245 /**
42246  * @class Roo.XTemplate
42247  * @extends Roo.Template
42248  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42249 <pre><code>
42250 var t = new Roo.XTemplate(
42251         '&lt;select name="{name}"&gt;',
42252                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42253         '&lt;/select&gt;'
42254 );
42255  
42256 // then append, applying the master template values
42257  </code></pre>
42258  *
42259  * Supported features:
42260  *
42261  *  Tags:
42262
42263 <pre><code>
42264       {a_variable} - output encoded.
42265       {a_variable.format:("Y-m-d")} - call a method on the variable
42266       {a_variable:raw} - unencoded output
42267       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42268       {a_variable:this.method_on_template(...)} - call a method on the template object.
42269  
42270 </code></pre>
42271  *  The tpl tag:
42272 <pre><code>
42273         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42274         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42275         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42276         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42277   
42278         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42279         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42280 </code></pre>
42281  *      
42282  */
42283 Roo.XTemplate = function()
42284 {
42285     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42286     if (this.html) {
42287         this.compile();
42288     }
42289 };
42290
42291
42292 Roo.extend(Roo.XTemplate, Roo.Template, {
42293
42294     /**
42295      * The various sub templates
42296      */
42297     tpls : false,
42298     /**
42299      *
42300      * basic tag replacing syntax
42301      * WORD:WORD()
42302      *
42303      * // you can fake an object call by doing this
42304      *  x.t:(test,tesT) 
42305      * 
42306      */
42307     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42308
42309     /**
42310      * compile the template
42311      *
42312      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42313      *
42314      */
42315     compile: function()
42316     {
42317         var s = this.html;
42318      
42319         s = ['<tpl>', s, '</tpl>'].join('');
42320     
42321         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42322             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42323             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42324             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42325             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42326             m,
42327             id     = 0,
42328             tpls   = [];
42329     
42330         while(true == !!(m = s.match(re))){
42331             var forMatch   = m[0].match(nameRe),
42332                 ifMatch   = m[0].match(ifRe),
42333                 execMatch   = m[0].match(execRe),
42334                 namedMatch   = m[0].match(namedRe),
42335                 
42336                 exp  = null, 
42337                 fn   = null,
42338                 exec = null,
42339                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42340                 
42341             if (ifMatch) {
42342                 // if - puts fn into test..
42343                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42344                 if(exp){
42345                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42346                 }
42347             }
42348             
42349             if (execMatch) {
42350                 // exec - calls a function... returns empty if true is  returned.
42351                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42352                 if(exp){
42353                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42354                 }
42355             }
42356             
42357             
42358             if (name) {
42359                 // for = 
42360                 switch(name){
42361                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42362                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42363                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42364                 }
42365             }
42366             var uid = namedMatch ? namedMatch[1] : id;
42367             
42368             
42369             tpls.push({
42370                 id:     namedMatch ? namedMatch[1] : id,
42371                 target: name,
42372                 exec:   exec,
42373                 test:   fn,
42374                 body:   m[1] || ''
42375             });
42376             if (namedMatch) {
42377                 s = s.replace(m[0], '');
42378             } else { 
42379                 s = s.replace(m[0], '{xtpl'+ id + '}');
42380             }
42381             ++id;
42382         }
42383         this.tpls = [];
42384         for(var i = tpls.length-1; i >= 0; --i){
42385             this.compileTpl(tpls[i]);
42386             this.tpls[tpls[i].id] = tpls[i];
42387         }
42388         this.master = tpls[tpls.length-1];
42389         return this;
42390     },
42391     /**
42392      * same as applyTemplate, except it's done to one of the subTemplates
42393      * when using named templates, you can do:
42394      *
42395      * var str = pl.applySubTemplate('your-name', values);
42396      *
42397      * 
42398      * @param {Number} id of the template
42399      * @param {Object} values to apply to template
42400      * @param {Object} parent (normaly the instance of this object)
42401      */
42402     applySubTemplate : function(id, values, parent)
42403     {
42404         
42405         
42406         var t = this.tpls[id];
42407         
42408         
42409         try { 
42410             if(t.test && !t.test.call(this, values, parent)){
42411                 return '';
42412             }
42413         } catch(e) {
42414             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42415             Roo.log(e.toString());
42416             Roo.log(t.test);
42417             return ''
42418         }
42419         try { 
42420             
42421             if(t.exec && t.exec.call(this, values, parent)){
42422                 return '';
42423             }
42424         } catch(e) {
42425             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42426             Roo.log(e.toString());
42427             Roo.log(t.exec);
42428             return ''
42429         }
42430         try {
42431             var vs = t.target ? t.target.call(this, values, parent) : values;
42432             parent = t.target ? values : parent;
42433             if(t.target && vs instanceof Array){
42434                 var buf = [];
42435                 for(var i = 0, len = vs.length; i < len; i++){
42436                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42437                 }
42438                 return buf.join('');
42439             }
42440             return t.compiled.call(this, vs, parent);
42441         } catch (e) {
42442             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42443             Roo.log(e.toString());
42444             Roo.log(t.compiled);
42445             return '';
42446         }
42447     },
42448
42449     compileTpl : function(tpl)
42450     {
42451         var fm = Roo.util.Format;
42452         var useF = this.disableFormats !== true;
42453         var sep = Roo.isGecko ? "+" : ",";
42454         var undef = function(str) {
42455             Roo.log("Property not found :"  + str);
42456             return '';
42457         };
42458         
42459         var fn = function(m, name, format, args)
42460         {
42461             //Roo.log(arguments);
42462             args = args ? args.replace(/\\'/g,"'") : args;
42463             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42464             if (typeof(format) == 'undefined') {
42465                 format= 'htmlEncode';
42466             }
42467             if (format == 'raw' ) {
42468                 format = false;
42469             }
42470             
42471             if(name.substr(0, 4) == 'xtpl'){
42472                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42473             }
42474             
42475             // build an array of options to determine if value is undefined..
42476             
42477             // basically get 'xxxx.yyyy' then do
42478             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42479             //    (function () { Roo.log("Property not found"); return ''; })() :
42480             //    ......
42481             
42482             var udef_ar = [];
42483             var lookfor = '';
42484             Roo.each(name.split('.'), function(st) {
42485                 lookfor += (lookfor.length ? '.': '') + st;
42486                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42487             });
42488             
42489             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42490             
42491             
42492             if(format && useF){
42493                 
42494                 args = args ? ',' + args : "";
42495                  
42496                 if(format.substr(0, 5) != "this."){
42497                     format = "fm." + format + '(';
42498                 }else{
42499                     format = 'this.call("'+ format.substr(5) + '", ';
42500                     args = ", values";
42501                 }
42502                 
42503                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42504             }
42505              
42506             if (args.length) {
42507                 // called with xxyx.yuu:(test,test)
42508                 // change to ()
42509                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42510             }
42511             // raw.. - :raw modifier..
42512             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42513             
42514         };
42515         var body;
42516         // branched to use + in gecko and [].join() in others
42517         if(Roo.isGecko){
42518             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42519                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42520                     "';};};";
42521         }else{
42522             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42523             body.push(tpl.body.replace(/(\r\n|\n)/g,
42524                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42525             body.push("'].join('');};};");
42526             body = body.join('');
42527         }
42528         
42529         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42530        
42531         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42532         eval(body);
42533         
42534         return this;
42535     },
42536
42537     applyTemplate : function(values){
42538         return this.master.compiled.call(this, values, {});
42539         //var s = this.subs;
42540     },
42541
42542     apply : function(){
42543         return this.applyTemplate.apply(this, arguments);
42544     }
42545
42546  });
42547
42548 Roo.XTemplate.from = function(el){
42549     el = Roo.getDom(el);
42550     return new Roo.XTemplate(el.value || el.innerHTML);
42551 };