roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75     if (config.listeners || config.events) { 
76         Roo.BasicLayoutRegion.superclass.constructor.call(this,  { 
77             listeners : config.listeners || {}, 
78             events : config.events || {} 
79         });    
80     }
81 };
82
83 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
84
85     /**
86      * The id of the element associated with this object.  This is what we
87      * refer to as the "linked element" because the size and position of
88      * this element is used to determine when the drag and drop objects have
89      * interacted.
90      * @property id
91      * @type String
92      */
93     id: null,
94
95     /**
96      * Configuration attributes passed into the constructor
97      * @property config
98      * @type object
99      */
100     config: null,
101
102     /**
103      * The id of the element that will be dragged.  By default this is same
104      * as the linked element , but could be changed to another element. Ex:
105      * Roo.dd.DDProxy
106      * @property dragElId
107      * @type String
108      * @private
109      */
110     dragElId: null,
111
112     /**
113      * the id of the element that initiates the drag operation.  By default
114      * this is the linked element, but could be changed to be a child of this
115      * element.  This lets us do things like only starting the drag when the
116      * header element within the linked html element is clicked.
117      * @property handleElId
118      * @type String
119      * @private
120      */
121     handleElId: null,
122
123     /**
124      * An associative array of HTML tags that will be ignored if clicked.
125      * @property invalidHandleTypes
126      * @type {string: string}
127      */
128     invalidHandleTypes: null,
129
130     /**
131      * An associative array of ids for elements that will be ignored if clicked
132      * @property invalidHandleIds
133      * @type {string: string}
134      */
135     invalidHandleIds: null,
136
137     /**
138      * An indexted array of css class names for elements that will be ignored
139      * if clicked.
140      * @property invalidHandleClasses
141      * @type string[]
142      */
143     invalidHandleClasses: null,
144
145     /**
146      * The linked element's absolute X position at the time the drag was
147      * started
148      * @property startPageX
149      * @type int
150      * @private
151      */
152     startPageX: 0,
153
154     /**
155      * The linked element's absolute X position at the time the drag was
156      * started
157      * @property startPageY
158      * @type int
159      * @private
160      */
161     startPageY: 0,
162
163     /**
164      * The group defines a logical collection of DragDrop objects that are
165      * related.  Instances only get events when interacting with other
166      * DragDrop object in the same group.  This lets us define multiple
167      * groups using a single DragDrop subclass if we want.
168      * @property groups
169      * @type {string: string}
170      */
171     groups: null,
172
173     /**
174      * Individual drag/drop instances can be locked.  This will prevent
175      * onmousedown start drag.
176      * @property locked
177      * @type boolean
178      * @private
179      */
180     locked: false,
181
182     /**
183      * Lock this instance
184      * @method lock
185      */
186     lock: function() { this.locked = true; },
187
188     /**
189      * Unlock this instace
190      * @method unlock
191      */
192     unlock: function() { this.locked = false; },
193
194     /**
195      * By default, all insances can be a drop target.  This can be disabled by
196      * setting isTarget to false.
197      * @method isTarget
198      * @type boolean
199      */
200     isTarget: true,
201
202     /**
203      * The padding configured for this drag and drop object for calculating
204      * the drop zone intersection with this object.
205      * @method padding
206      * @type int[]
207      */
208     padding: null,
209
210     /**
211      * Cached reference to the linked element
212      * @property _domRef
213      * @private
214      */
215     _domRef: null,
216
217     /**
218      * Internal typeof flag
219      * @property __ygDragDrop
220      * @private
221      */
222     __ygDragDrop: true,
223
224     /**
225      * Set to true when horizontal contraints are applied
226      * @property constrainX
227      * @type boolean
228      * @private
229      */
230     constrainX: false,
231
232     /**
233      * Set to true when vertical contraints are applied
234      * @property constrainY
235      * @type boolean
236      * @private
237      */
238     constrainY: false,
239
240     /**
241      * The left constraint
242      * @property minX
243      * @type int
244      * @private
245      */
246     minX: 0,
247
248     /**
249      * The right constraint
250      * @property maxX
251      * @type int
252      * @private
253      */
254     maxX: 0,
255
256     /**
257      * The up constraint
258      * @property minY
259      * @type int
260      * @type int
261      * @private
262      */
263     minY: 0,
264
265     /**
266      * The down constraint
267      * @property maxY
268      * @type int
269      * @private
270      */
271     maxY: 0,
272
273     /**
274      * Maintain offsets when we resetconstraints.  Set to true when you want
275      * the position of the element relative to its parent to stay the same
276      * when the page changes
277      *
278      * @property maintainOffset
279      * @type boolean
280      */
281     maintainOffset: false,
282
283     /**
284      * Array of pixel locations the element will snap to if we specified a
285      * horizontal graduation/interval.  This array is generated automatically
286      * when you define a tick interval.
287      * @property xTicks
288      * @type int[]
289      */
290     xTicks: null,
291
292     /**
293      * Array of pixel locations the element will snap to if we specified a
294      * vertical graduation/interval.  This array is generated automatically
295      * when you define a tick interval.
296      * @property yTicks
297      * @type int[]
298      */
299     yTicks: null,
300
301     /**
302      * By default the drag and drop instance will only respond to the primary
303      * button click (left button for a right-handed mouse).  Set to true to
304      * allow drag and drop to start with any mouse click that is propogated
305      * by the browser
306      * @property primaryButtonOnly
307      * @type boolean
308      */
309     primaryButtonOnly: true,
310
311     /**
312      * The availabe property is false until the linked dom element is accessible.
313      * @property available
314      * @type boolean
315      */
316     available: false,
317
318     /**
319      * By default, drags can only be initiated if the mousedown occurs in the
320      * region the linked element is.  This is done in part to work around a
321      * bug in some browsers that mis-report the mousedown if the previous
322      * mouseup happened outside of the window.  This property is set to true
323      * if outer handles are defined.
324      *
325      * @property hasOuterHandles
326      * @type boolean
327      * @default false
328      */
329     hasOuterHandles: false,
330
331     /**
332      * Code that executes immediately before the startDrag event
333      * @method b4StartDrag
334      * @private
335      */
336     b4StartDrag: function(x, y) { },
337
338     /**
339      * Abstract method called after a drag/drop object is clicked
340      * and the drag or mousedown time thresholds have beeen met.
341      * @method startDrag
342      * @param {int} X click location
343      * @param {int} Y click location
344      */
345     startDrag: function(x, y) { /* override this */ },
346
347     /**
348      * Code that executes immediately before the onDrag event
349      * @method b4Drag
350      * @private
351      */
352     b4Drag: function(e) { },
353
354     /**
355      * Abstract method called during the onMouseMove event while dragging an
356      * object.
357      * @method onDrag
358      * @param {Event} e the mousemove event
359      */
360     onDrag: function(e) { /* override this */ },
361
362     /**
363      * Abstract method called when this element fist begins hovering over
364      * another DragDrop obj
365      * @method onDragEnter
366      * @param {Event} e the mousemove event
367      * @param {String|DragDrop[]} id In POINT mode, the element
368      * id this is hovering over.  In INTERSECT mode, an array of one or more
369      * dragdrop items being hovered over.
370      */
371     onDragEnter: function(e, id) { /* override this */ },
372
373     /**
374      * Code that executes immediately before the onDragOver event
375      * @method b4DragOver
376      * @private
377      */
378     b4DragOver: function(e) { },
379
380     /**
381      * Abstract method called when this element is hovering over another
382      * DragDrop obj
383      * @method onDragOver
384      * @param {Event} e the mousemove event
385      * @param {String|DragDrop[]} id In POINT mode, the element
386      * id this is hovering over.  In INTERSECT mode, an array of dd items
387      * being hovered over.
388      */
389     onDragOver: function(e, id) { /* override this */ },
390
391     /**
392      * Code that executes immediately before the onDragOut event
393      * @method b4DragOut
394      * @private
395      */
396     b4DragOut: function(e) { },
397
398     /**
399      * Abstract method called when we are no longer hovering over an element
400      * @method onDragOut
401      * @param {Event} e the mousemove event
402      * @param {String|DragDrop[]} id In POINT mode, the element
403      * id this was hovering over.  In INTERSECT mode, an array of dd items
404      * that the mouse is no longer over.
405      */
406     onDragOut: function(e, id) { /* override this */ },
407
408     /**
409      * Code that executes immediately before the onDragDrop event
410      * @method b4DragDrop
411      * @private
412      */
413     b4DragDrop: function(e) { },
414
415     /**
416      * Abstract method called when this item is dropped on another DragDrop
417      * obj
418      * @method onDragDrop
419      * @param {Event} e the mouseup event
420      * @param {String|DragDrop[]} id In POINT mode, the element
421      * id this was dropped on.  In INTERSECT mode, an array of dd items this
422      * was dropped on.
423      */
424     onDragDrop: function(e, id) { /* override this */ },
425
426     /**
427      * Abstract method called when this item is dropped on an area with no
428      * drop target
429      * @method onInvalidDrop
430      * @param {Event} e the mouseup event
431      */
432     onInvalidDrop: function(e) { /* override this */ },
433
434     /**
435      * Code that executes immediately before the endDrag event
436      * @method b4EndDrag
437      * @private
438      */
439     b4EndDrag: function(e) { },
440
441     /**
442      * Fired when we are done dragging the object
443      * @method endDrag
444      * @param {Event} e the mouseup event
445      */
446     endDrag: function(e) { /* override this */ },
447
448     /**
449      * Code executed immediately before the onMouseDown event
450      * @method b4MouseDown
451      * @param {Event} e the mousedown event
452      * @private
453      */
454     b4MouseDown: function(e) {  },
455
456     /**
457      * Event handler that fires when a drag/drop obj gets a mousedown
458      * @method onMouseDown
459      * @param {Event} e the mousedown event
460      */
461     onMouseDown: function(e) { /* override this */ },
462
463     /**
464      * Event handler that fires when a drag/drop obj gets a mouseup
465      * @method onMouseUp
466      * @param {Event} e the mouseup event
467      */
468     onMouseUp: function(e) { /* override this */ },
469
470     /**
471      * Override the onAvailable method to do what is needed after the initial
472      * position was determined.
473      * @method onAvailable
474      */
475     onAvailable: function () {
476     },
477
478     /*
479      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
480      * @type Object
481      */
482     defaultPadding : {left:0, right:0, top:0, bottom:0},
483
484     /*
485      * Initializes the drag drop object's constraints to restrict movement to a certain element.
486  *
487  * Usage:
488  <pre><code>
489  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
490                 { dragElId: "existingProxyDiv" });
491  dd.startDrag = function(){
492      this.constrainTo("parent-id");
493  };
494  </code></pre>
495  * Or you can initalize it using the {@link Roo.Element} object:
496  <pre><code>
497  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
498      startDrag : function(){
499          this.constrainTo("parent-id");
500      }
501  });
502  </code></pre>
503      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
504      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
505      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
506      * an object containing the sides to pad. For example: {right:10, bottom:10}
507      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
508      */
509     constrainTo : function(constrainTo, pad, inContent){
510         if(typeof pad == "number"){
511             pad = {left: pad, right:pad, top:pad, bottom:pad};
512         }
513         pad = pad || this.defaultPadding;
514         var b = Roo.get(this.getEl()).getBox();
515         var ce = Roo.get(constrainTo);
516         var s = ce.getScroll();
517         var c, cd = ce.dom;
518         if(cd == document.body){
519             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
520         }else{
521             xy = ce.getXY();
522             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
523         }
524
525
526         var topSpace = b.y - c.y;
527         var leftSpace = b.x - c.x;
528
529         this.resetConstraints();
530         this.setXConstraint(leftSpace - (pad.left||0), // left
531                 c.width - leftSpace - b.width - (pad.right||0) //right
532         );
533         this.setYConstraint(topSpace - (pad.top||0), //top
534                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
535         );
536     },
537
538     /**
539      * Returns a reference to the linked element
540      * @method getEl
541      * @return {HTMLElement} the html element
542      */
543     getEl: function() {
544         if (!this._domRef) {
545             this._domRef = Roo.getDom(this.id);
546         }
547
548         return this._domRef;
549     },
550
551     /**
552      * Returns a reference to the actual element to drag.  By default this is
553      * the same as the html element, but it can be assigned to another
554      * element. An example of this can be found in Roo.dd.DDProxy
555      * @method getDragEl
556      * @return {HTMLElement} the html element
557      */
558     getDragEl: function() {
559         return Roo.getDom(this.dragElId);
560     },
561
562     /**
563      * Sets up the DragDrop object.  Must be called in the constructor of any
564      * Roo.dd.DragDrop subclass
565      * @method init
566      * @param id the id of the linked element
567      * @param {String} sGroup the group of related items
568      * @param {object} config configuration attributes
569      */
570     init: function(id, sGroup, config) {
571         this.initTarget(id, sGroup, config);
572         Event.on(this.id, "mousedown", this.handleMouseDown, this);
573         // Event.on(this.id, "selectstart", Event.preventDefault);
574     },
575
576     /**
577      * Initializes Targeting functionality only... the object does not
578      * get a mousedown handler.
579      * @method initTarget
580      * @param id the id of the linked element
581      * @param {String} sGroup the group of related items
582      * @param {object} config configuration attributes
583      */
584     initTarget: function(id, sGroup, config) {
585
586         // configuration attributes
587         this.config = config || {};
588
589         // create a local reference to the drag and drop manager
590         this.DDM = Roo.dd.DDM;
591         // initialize the groups array
592         this.groups = {};
593
594         // assume that we have an element reference instead of an id if the
595         // parameter is not a string
596         if (typeof id !== "string") {
597             id = Roo.id(id);
598         }
599
600         // set the id
601         this.id = id;
602
603         // add to an interaction group
604         this.addToGroup((sGroup) ? sGroup : "default");
605
606         // We don't want to register this as the handle with the manager
607         // so we just set the id rather than calling the setter.
608         this.handleElId = id;
609
610         // the linked element is the element that gets dragged by default
611         this.setDragElId(id);
612
613         // by default, clicked anchors will not start drag operations.
614         this.invalidHandleTypes = { A: "A" };
615         this.invalidHandleIds = {};
616         this.invalidHandleClasses = [];
617
618         this.applyConfig();
619
620         this.handleOnAvailable();
621     },
622
623     /**
624      * Applies the configuration parameters that were passed into the constructor.
625      * This is supposed to happen at each level through the inheritance chain.  So
626      * a DDProxy implentation will execute apply config on DDProxy, DD, and
627      * DragDrop in order to get all of the parameters that are available in
628      * each object.
629      * @method applyConfig
630      */
631     applyConfig: function() {
632
633         // configurable properties:
634         //    padding, isTarget, maintainOffset, primaryButtonOnly
635         this.padding           = this.config.padding || [0, 0, 0, 0];
636         this.isTarget          = (this.config.isTarget !== false);
637         this.maintainOffset    = (this.config.maintainOffset);
638         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
639
640     },
641
642     /**
643      * Executed when the linked element is available
644      * @method handleOnAvailable
645      * @private
646      */
647     handleOnAvailable: function() {
648         this.available = true;
649         this.resetConstraints();
650         this.onAvailable();
651     },
652
653      /**
654      * Configures the padding for the target zone in px.  Effectively expands
655      * (or reduces) the virtual object size for targeting calculations.
656      * Supports css-style shorthand; if only one parameter is passed, all sides
657      * will have that padding, and if only two are passed, the top and bottom
658      * will have the first param, the left and right the second.
659      * @method setPadding
660      * @param {int} iTop    Top pad
661      * @param {int} iRight  Right pad
662      * @param {int} iBot    Bot pad
663      * @param {int} iLeft   Left pad
664      */
665     setPadding: function(iTop, iRight, iBot, iLeft) {
666         // this.padding = [iLeft, iRight, iTop, iBot];
667         if (!iRight && 0 !== iRight) {
668             this.padding = [iTop, iTop, iTop, iTop];
669         } else if (!iBot && 0 !== iBot) {
670             this.padding = [iTop, iRight, iTop, iRight];
671         } else {
672             this.padding = [iTop, iRight, iBot, iLeft];
673         }
674     },
675
676     /**
677      * Stores the initial placement of the linked element.
678      * @method setInitialPosition
679      * @param {int} diffX   the X offset, default 0
680      * @param {int} diffY   the Y offset, default 0
681      */
682     setInitPosition: function(diffX, diffY) {
683         var el = this.getEl();
684
685         if (!this.DDM.verifyEl(el)) {
686             return;
687         }
688
689         var dx = diffX || 0;
690         var dy = diffY || 0;
691
692         var p = Dom.getXY( el );
693
694         this.initPageX = p[0] - dx;
695         this.initPageY = p[1] - dy;
696
697         this.lastPageX = p[0];
698         this.lastPageY = p[1];
699
700
701         this.setStartPosition(p);
702     },
703
704     /**
705      * Sets the start position of the element.  This is set when the obj
706      * is initialized, the reset when a drag is started.
707      * @method setStartPosition
708      * @param pos current position (from previous lookup)
709      * @private
710      */
711     setStartPosition: function(pos) {
712         var p = pos || Dom.getXY( this.getEl() );
713         this.deltaSetXY = null;
714
715         this.startPageX = p[0];
716         this.startPageY = p[1];
717     },
718
719     /**
720      * Add this instance to a group of related drag/drop objects.  All
721      * instances belong to at least one group, and can belong to as many
722      * groups as needed.
723      * @method addToGroup
724      * @param sGroup {string} the name of the group
725      */
726     addToGroup: function(sGroup) {
727         this.groups[sGroup] = true;
728         this.DDM.regDragDrop(this, sGroup);
729     },
730
731     /**
732      * Remove's this instance from the supplied interaction group
733      * @method removeFromGroup
734      * @param {string}  sGroup  The group to drop
735      */
736     removeFromGroup: function(sGroup) {
737         if (this.groups[sGroup]) {
738             delete this.groups[sGroup];
739         }
740
741         this.DDM.removeDDFromGroup(this, sGroup);
742     },
743
744     /**
745      * Allows you to specify that an element other than the linked element
746      * will be moved with the cursor during a drag
747      * @method setDragElId
748      * @param id {string} the id of the element that will be used to initiate the drag
749      */
750     setDragElId: function(id) {
751         this.dragElId = id;
752     },
753
754     /**
755      * Allows you to specify a child of the linked element that should be
756      * used to initiate the drag operation.  An example of this would be if
757      * you have a content div with text and links.  Clicking anywhere in the
758      * content area would normally start the drag operation.  Use this method
759      * to specify that an element inside of the content div is the element
760      * that starts the drag operation.
761      * @method setHandleElId
762      * @param id {string} the id of the element that will be used to
763      * initiate the drag.
764      */
765     setHandleElId: function(id) {
766         if (typeof id !== "string") {
767             id = Roo.id(id);
768         }
769         this.handleElId = id;
770         this.DDM.regHandle(this.id, id);
771     },
772
773     /**
774      * Allows you to set an element outside of the linked element as a drag
775      * handle
776      * @method setOuterHandleElId
777      * @param id the id of the element that will be used to initiate the drag
778      */
779     setOuterHandleElId: function(id) {
780         if (typeof id !== "string") {
781             id = Roo.id(id);
782         }
783         Event.on(id, "mousedown",
784                 this.handleMouseDown, this);
785         this.setHandleElId(id);
786
787         this.hasOuterHandles = true;
788     },
789
790     /**
791      * Remove all drag and drop hooks for this element
792      * @method unreg
793      */
794     unreg: function() {
795         Event.un(this.id, "mousedown",
796                 this.handleMouseDown);
797         this._domRef = null;
798         this.DDM._remove(this);
799     },
800
801     destroy : function(){
802         this.unreg();
803     },
804
805     /**
806      * Returns true if this instance is locked, or the drag drop mgr is locked
807      * (meaning that all drag/drop is disabled on the page.)
808      * @method isLocked
809      * @return {boolean} true if this obj or all drag/drop is locked, else
810      * false
811      */
812     isLocked: function() {
813         return (this.DDM.isLocked() || this.locked);
814     },
815
816     /**
817      * Fired when this object is clicked
818      * @method handleMouseDown
819      * @param {Event} e
820      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
821      * @private
822      */
823     handleMouseDown: function(e, oDD){
824         if (this.primaryButtonOnly && e.button != 0) {
825             return;
826         }
827
828         if (this.isLocked()) {
829             return;
830         }
831
832         this.DDM.refreshCache(this.groups);
833
834         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
835         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
836         } else {
837             if (this.clickValidator(e)) {
838
839                 // set the initial element position
840                 this.setStartPosition();
841
842
843                 this.b4MouseDown(e);
844                 this.onMouseDown(e);
845
846                 this.DDM.handleMouseDown(e, this);
847
848                 this.DDM.stopEvent(e);
849             } else {
850
851
852             }
853         }
854     },
855
856     clickValidator: function(e) {
857         var target = e.getTarget();
858         return ( this.isValidHandleChild(target) &&
859                     (this.id == this.handleElId ||
860                         this.DDM.handleWasClicked(target, this.id)) );
861     },
862
863     /**
864      * Allows you to specify a tag name that should not start a drag operation
865      * when clicked.  This is designed to facilitate embedding links within a
866      * drag handle that do something other than start the drag.
867      * @method addInvalidHandleType
868      * @param {string} tagName the type of element to exclude
869      */
870     addInvalidHandleType: function(tagName) {
871         var type = tagName.toUpperCase();
872         this.invalidHandleTypes[type] = type;
873     },
874
875     /**
876      * Lets you to specify an element id for a child of a drag handle
877      * that should not initiate a drag
878      * @method addInvalidHandleId
879      * @param {string} id the element id of the element you wish to ignore
880      */
881     addInvalidHandleId: function(id) {
882         if (typeof id !== "string") {
883             id = Roo.id(id);
884         }
885         this.invalidHandleIds[id] = id;
886     },
887
888     /**
889      * Lets you specify a css class of elements that will not initiate a drag
890      * @method addInvalidHandleClass
891      * @param {string} cssClass the class of the elements you wish to ignore
892      */
893     addInvalidHandleClass: function(cssClass) {
894         this.invalidHandleClasses.push(cssClass);
895     },
896
897     /**
898      * Unsets an excluded tag name set by addInvalidHandleType
899      * @method removeInvalidHandleType
900      * @param {string} tagName the type of element to unexclude
901      */
902     removeInvalidHandleType: function(tagName) {
903         var type = tagName.toUpperCase();
904         // this.invalidHandleTypes[type] = null;
905         delete this.invalidHandleTypes[type];
906     },
907
908     /**
909      * Unsets an invalid handle id
910      * @method removeInvalidHandleId
911      * @param {string} id the id of the element to re-enable
912      */
913     removeInvalidHandleId: function(id) {
914         if (typeof id !== "string") {
915             id = Roo.id(id);
916         }
917         delete this.invalidHandleIds[id];
918     },
919
920     /**
921      * Unsets an invalid css class
922      * @method removeInvalidHandleClass
923      * @param {string} cssClass the class of the element(s) you wish to
924      * re-enable
925      */
926     removeInvalidHandleClass: function(cssClass) {
927         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
928             if (this.invalidHandleClasses[i] == cssClass) {
929                 delete this.invalidHandleClasses[i];
930             }
931         }
932     },
933
934     /**
935      * Checks the tag exclusion list to see if this click should be ignored
936      * @method isValidHandleChild
937      * @param {HTMLElement} node the HTMLElement to evaluate
938      * @return {boolean} true if this is a valid tag type, false if not
939      */
940     isValidHandleChild: function(node) {
941
942         var valid = true;
943         // var n = (node.nodeName == "#text") ? node.parentNode : node;
944         var nodeName;
945         try {
946             nodeName = node.nodeName.toUpperCase();
947         } catch(e) {
948             nodeName = node.nodeName;
949         }
950         valid = valid && !this.invalidHandleTypes[nodeName];
951         valid = valid && !this.invalidHandleIds[node.id];
952
953         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
954             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
955         }
956
957
958         return valid;
959
960     },
961
962     /**
963      * Create the array of horizontal tick marks if an interval was specified
964      * in setXConstraint().
965      * @method setXTicks
966      * @private
967      */
968     setXTicks: function(iStartX, iTickSize) {
969         this.xTicks = [];
970         this.xTickSize = iTickSize;
971
972         var tickMap = {};
973
974         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
975             if (!tickMap[i]) {
976                 this.xTicks[this.xTicks.length] = i;
977                 tickMap[i] = true;
978             }
979         }
980
981         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
982             if (!tickMap[i]) {
983                 this.xTicks[this.xTicks.length] = i;
984                 tickMap[i] = true;
985             }
986         }
987
988         this.xTicks.sort(this.DDM.numericSort) ;
989     },
990
991     /**
992      * Create the array of vertical tick marks if an interval was specified in
993      * setYConstraint().
994      * @method setYTicks
995      * @private
996      */
997     setYTicks: function(iStartY, iTickSize) {
998         this.yTicks = [];
999         this.yTickSize = iTickSize;
1000
1001         var tickMap = {};
1002
1003         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1004             if (!tickMap[i]) {
1005                 this.yTicks[this.yTicks.length] = i;
1006                 tickMap[i] = true;
1007             }
1008         }
1009
1010         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1011             if (!tickMap[i]) {
1012                 this.yTicks[this.yTicks.length] = i;
1013                 tickMap[i] = true;
1014             }
1015         }
1016
1017         this.yTicks.sort(this.DDM.numericSort) ;
1018     },
1019
1020     /**
1021      * By default, the element can be dragged any place on the screen.  Use
1022      * this method to limit the horizontal travel of the element.  Pass in
1023      * 0,0 for the parameters if you want to lock the drag to the y axis.
1024      * @method setXConstraint
1025      * @param {int} iLeft the number of pixels the element can move to the left
1026      * @param {int} iRight the number of pixels the element can move to the
1027      * right
1028      * @param {int} iTickSize optional parameter for specifying that the
1029      * element
1030      * should move iTickSize pixels at a time.
1031      */
1032     setXConstraint: function(iLeft, iRight, iTickSize) {
1033         this.leftConstraint = iLeft;
1034         this.rightConstraint = iRight;
1035
1036         this.minX = this.initPageX - iLeft;
1037         this.maxX = this.initPageX + iRight;
1038         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1039
1040         this.constrainX = true;
1041     },
1042
1043     /**
1044      * Clears any constraints applied to this instance.  Also clears ticks
1045      * since they can't exist independent of a constraint at this time.
1046      * @method clearConstraints
1047      */
1048     clearConstraints: function() {
1049         this.constrainX = false;
1050         this.constrainY = false;
1051         this.clearTicks();
1052     },
1053
1054     /**
1055      * Clears any tick interval defined for this instance
1056      * @method clearTicks
1057      */
1058     clearTicks: function() {
1059         this.xTicks = null;
1060         this.yTicks = null;
1061         this.xTickSize = 0;
1062         this.yTickSize = 0;
1063     },
1064
1065     /**
1066      * By default, the element can be dragged any place on the screen.  Set
1067      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1068      * parameters if you want to lock the drag to the x axis.
1069      * @method setYConstraint
1070      * @param {int} iUp the number of pixels the element can move up
1071      * @param {int} iDown the number of pixels the element can move down
1072      * @param {int} iTickSize optional parameter for specifying that the
1073      * element should move iTickSize pixels at a time.
1074      */
1075     setYConstraint: function(iUp, iDown, iTickSize) {
1076         this.topConstraint = iUp;
1077         this.bottomConstraint = iDown;
1078
1079         this.minY = this.initPageY - iUp;
1080         this.maxY = this.initPageY + iDown;
1081         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1082
1083         this.constrainY = true;
1084
1085     },
1086
1087     /**
1088      * resetConstraints must be called if you manually reposition a dd element.
1089      * @method resetConstraints
1090      * @param {boolean} maintainOffset
1091      */
1092     resetConstraints: function() {
1093
1094
1095         // Maintain offsets if necessary
1096         if (this.initPageX || this.initPageX === 0) {
1097             // figure out how much this thing has moved
1098             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1099             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1100
1101             this.setInitPosition(dx, dy);
1102
1103         // This is the first time we have detected the element's position
1104         } else {
1105             this.setInitPosition();
1106         }
1107
1108         if (this.constrainX) {
1109             this.setXConstraint( this.leftConstraint,
1110                                  this.rightConstraint,
1111                                  this.xTickSize        );
1112         }
1113
1114         if (this.constrainY) {
1115             this.setYConstraint( this.topConstraint,
1116                                  this.bottomConstraint,
1117                                  this.yTickSize         );
1118         }
1119     },
1120
1121     /**
1122      * Normally the drag element is moved pixel by pixel, but we can specify
1123      * that it move a number of pixels at a time.  This method resolves the
1124      * location when we have it set up like this.
1125      * @method getTick
1126      * @param {int} val where we want to place the object
1127      * @param {int[]} tickArray sorted array of valid points
1128      * @return {int} the closest tick
1129      * @private
1130      */
1131     getTick: function(val, tickArray) {
1132
1133         if (!tickArray) {
1134             // If tick interval is not defined, it is effectively 1 pixel,
1135             // so we return the value passed to us.
1136             return val;
1137         } else if (tickArray[0] >= val) {
1138             // The value is lower than the first tick, so we return the first
1139             // tick.
1140             return tickArray[0];
1141         } else {
1142             for (var i=0, len=tickArray.length; i<len; ++i) {
1143                 var next = i + 1;
1144                 if (tickArray[next] && tickArray[next] >= val) {
1145                     var diff1 = val - tickArray[i];
1146                     var diff2 = tickArray[next] - val;
1147                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1148                 }
1149             }
1150
1151             // The value is larger than the last tick, so we return the last
1152             // tick.
1153             return tickArray[tickArray.length - 1];
1154         }
1155     },
1156
1157     /**
1158      * toString method
1159      * @method toString
1160      * @return {string} string representation of the dd obj
1161      */
1162     toString: function() {
1163         return ("DragDrop " + this.id);
1164     }
1165
1166 });
1167
1168 })();
1169 /*
1170  * Based on:
1171  * Ext JS Library 1.1.1
1172  * Copyright(c) 2006-2007, Ext JS, LLC.
1173  *
1174  * Originally Released Under LGPL - original licence link has changed is not relivant.
1175  *
1176  * Fork - LGPL
1177  * <script type="text/javascript">
1178  */
1179
1180
1181 /**
1182  * The drag and drop utility provides a framework for building drag and drop
1183  * applications.  In addition to enabling drag and drop for specific elements,
1184  * the drag and drop elements are tracked by the manager class, and the
1185  * interactions between the various elements are tracked during the drag and
1186  * the implementing code is notified about these important moments.
1187  */
1188
1189 // Only load the library once.  Rewriting the manager class would orphan
1190 // existing drag and drop instances.
1191 if (!Roo.dd.DragDropMgr) {
1192
1193 /**
1194  * @class Roo.dd.DragDropMgr
1195  * DragDropMgr is a singleton that tracks the element interaction for
1196  * all DragDrop items in the window.  Generally, you will not call
1197  * this class directly, but it does have helper methods that could
1198  * be useful in your DragDrop implementations.
1199  * @singleton
1200  */
1201 Roo.dd.DragDropMgr = function() {
1202
1203     var Event = Roo.EventManager;
1204
1205     return {
1206
1207         /**
1208          * Two dimensional Array of registered DragDrop objects.  The first
1209          * dimension is the DragDrop item group, the second the DragDrop
1210          * object.
1211          * @property ids
1212          * @type {string: string}
1213          * @private
1214          * @static
1215          */
1216         ids: {},
1217
1218         /**
1219          * Array of element ids defined as drag handles.  Used to determine
1220          * if the element that generated the mousedown event is actually the
1221          * handle and not the html element itself.
1222          * @property handleIds
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         handleIds: {},
1228
1229         /**
1230          * the DragDrop object that is currently being dragged
1231          * @property dragCurrent
1232          * @type DragDrop
1233          * @private
1234          * @static
1235          **/
1236         dragCurrent: null,
1237
1238         /**
1239          * the DragDrop object(s) that are being hovered over
1240          * @property dragOvers
1241          * @type Array
1242          * @private
1243          * @static
1244          */
1245         dragOvers: {},
1246
1247         /**
1248          * the X distance between the cursor and the object being dragged
1249          * @property deltaX
1250          * @type int
1251          * @private
1252          * @static
1253          */
1254         deltaX: 0,
1255
1256         /**
1257          * the Y distance between the cursor and the object being dragged
1258          * @property deltaY
1259          * @type int
1260          * @private
1261          * @static
1262          */
1263         deltaY: 0,
1264
1265         /**
1266          * Flag to determine if we should prevent the default behavior of the
1267          * events we define. By default this is true, but this can be set to
1268          * false if you need the default behavior (not recommended)
1269          * @property preventDefault
1270          * @type boolean
1271          * @static
1272          */
1273         preventDefault: true,
1274
1275         /**
1276          * Flag to determine if we should stop the propagation of the events
1277          * we generate. This is true by default but you may want to set it to
1278          * false if the html element contains other features that require the
1279          * mouse click.
1280          * @property stopPropagation
1281          * @type boolean
1282          * @static
1283          */
1284         stopPropagation: true,
1285
1286         /**
1287          * Internal flag that is set to true when drag and drop has been
1288          * intialized
1289          * @property initialized
1290          * @private
1291          * @static
1292          */
1293         initalized: false,
1294
1295         /**
1296          * All drag and drop can be disabled.
1297          * @property locked
1298          * @private
1299          * @static
1300          */
1301         locked: false,
1302
1303         /**
1304          * Called the first time an element is registered.
1305          * @method init
1306          * @private
1307          * @static
1308          */
1309         init: function() {
1310             this.initialized = true;
1311         },
1312
1313         /**
1314          * In point mode, drag and drop interaction is defined by the
1315          * location of the cursor during the drag/drop
1316          * @property POINT
1317          * @type int
1318          * @static
1319          */
1320         POINT: 0,
1321
1322         /**
1323          * In intersect mode, drag and drop interactio nis defined by the
1324          * overlap of two or more drag and drop objects.
1325          * @property INTERSECT
1326          * @type int
1327          * @static
1328          */
1329         INTERSECT: 1,
1330
1331         /**
1332          * The current drag and drop mode.  Default: POINT
1333          * @property mode
1334          * @type int
1335          * @static
1336          */
1337         mode: 0,
1338
1339         /**
1340          * Runs method on all drag and drop objects
1341          * @method _execOnAll
1342          * @private
1343          * @static
1344          */
1345         _execOnAll: function(sMethod, args) {
1346             for (var i in this.ids) {
1347                 for (var j in this.ids[i]) {
1348                     var oDD = this.ids[i][j];
1349                     if (! this.isTypeOfDD(oDD)) {
1350                         continue;
1351                     }
1352                     oDD[sMethod].apply(oDD, args);
1353                 }
1354             }
1355         },
1356
1357         /**
1358          * Drag and drop initialization.  Sets up the global event handlers
1359          * @method _onLoad
1360          * @private
1361          * @static
1362          */
1363         _onLoad: function() {
1364
1365             this.init();
1366
1367
1368             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1369             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1370             Event.on(window,   "unload",    this._onUnload, this, true);
1371             Event.on(window,   "resize",    this._onResize, this, true);
1372             // Event.on(window,   "mouseout",    this._test);
1373
1374         },
1375
1376         /**
1377          * Reset constraints on all drag and drop objs
1378          * @method _onResize
1379          * @private
1380          * @static
1381          */
1382         _onResize: function(e) {
1383             this._execOnAll("resetConstraints", []);
1384         },
1385
1386         /**
1387          * Lock all drag and drop functionality
1388          * @method lock
1389          * @static
1390          */
1391         lock: function() { this.locked = true; },
1392
1393         /**
1394          * Unlock all drag and drop functionality
1395          * @method unlock
1396          * @static
1397          */
1398         unlock: function() { this.locked = false; },
1399
1400         /**
1401          * Is drag and drop locked?
1402          * @method isLocked
1403          * @return {boolean} True if drag and drop is locked, false otherwise.
1404          * @static
1405          */
1406         isLocked: function() { return this.locked; },
1407
1408         /**
1409          * Location cache that is set for all drag drop objects when a drag is
1410          * initiated, cleared when the drag is finished.
1411          * @property locationCache
1412          * @private
1413          * @static
1414          */
1415         locationCache: {},
1416
1417         /**
1418          * Set useCache to false if you want to force object the lookup of each
1419          * drag and drop linked element constantly during a drag.
1420          * @property useCache
1421          * @type boolean
1422          * @static
1423          */
1424         useCache: true,
1425
1426         /**
1427          * The number of pixels that the mouse needs to move after the
1428          * mousedown before the drag is initiated.  Default=3;
1429          * @property clickPixelThresh
1430          * @type int
1431          * @static
1432          */
1433         clickPixelThresh: 3,
1434
1435         /**
1436          * The number of milliseconds after the mousedown event to initiate the
1437          * drag if we don't get a mouseup event. Default=1000
1438          * @property clickTimeThresh
1439          * @type int
1440          * @static
1441          */
1442         clickTimeThresh: 350,
1443
1444         /**
1445          * Flag that indicates that either the drag pixel threshold or the
1446          * mousdown time threshold has been met
1447          * @property dragThreshMet
1448          * @type boolean
1449          * @private
1450          * @static
1451          */
1452         dragThreshMet: false,
1453
1454         /**
1455          * Timeout used for the click time threshold
1456          * @property clickTimeout
1457          * @type Object
1458          * @private
1459          * @static
1460          */
1461         clickTimeout: null,
1462
1463         /**
1464          * The X position of the mousedown event stored for later use when a
1465          * drag threshold is met.
1466          * @property startX
1467          * @type int
1468          * @private
1469          * @static
1470          */
1471         startX: 0,
1472
1473         /**
1474          * The Y position of the mousedown event stored for later use when a
1475          * drag threshold is met.
1476          * @property startY
1477          * @type int
1478          * @private
1479          * @static
1480          */
1481         startY: 0,
1482
1483         /**
1484          * Each DragDrop instance must be registered with the DragDropMgr.
1485          * This is executed in DragDrop.init()
1486          * @method regDragDrop
1487          * @param {DragDrop} oDD the DragDrop object to register
1488          * @param {String} sGroup the name of the group this element belongs to
1489          * @static
1490          */
1491         regDragDrop: function(oDD, sGroup) {
1492             if (!this.initialized) { this.init(); }
1493
1494             if (!this.ids[sGroup]) {
1495                 this.ids[sGroup] = {};
1496             }
1497             this.ids[sGroup][oDD.id] = oDD;
1498         },
1499
1500         /**
1501          * Removes the supplied dd instance from the supplied group. Executed
1502          * by DragDrop.removeFromGroup, so don't call this function directly.
1503          * @method removeDDFromGroup
1504          * @private
1505          * @static
1506          */
1507         removeDDFromGroup: function(oDD, sGroup) {
1508             if (!this.ids[sGroup]) {
1509                 this.ids[sGroup] = {};
1510             }
1511
1512             var obj = this.ids[sGroup];
1513             if (obj && obj[oDD.id]) {
1514                 delete obj[oDD.id];
1515             }
1516         },
1517
1518         /**
1519          * Unregisters a drag and drop item.  This is executed in
1520          * DragDrop.unreg, use that method instead of calling this directly.
1521          * @method _remove
1522          * @private
1523          * @static
1524          */
1525         _remove: function(oDD) {
1526             for (var g in oDD.groups) {
1527                 if (g && this.ids[g][oDD.id]) {
1528                     delete this.ids[g][oDD.id];
1529                 }
1530             }
1531             delete this.handleIds[oDD.id];
1532         },
1533
1534         /**
1535          * Each DragDrop handle element must be registered.  This is done
1536          * automatically when executing DragDrop.setHandleElId()
1537          * @method regHandle
1538          * @param {String} sDDId the DragDrop id this element is a handle for
1539          * @param {String} sHandleId the id of the element that is the drag
1540          * handle
1541          * @static
1542          */
1543         regHandle: function(sDDId, sHandleId) {
1544             if (!this.handleIds[sDDId]) {
1545                 this.handleIds[sDDId] = {};
1546             }
1547             this.handleIds[sDDId][sHandleId] = sHandleId;
1548         },
1549
1550         /**
1551          * Utility function to determine if a given element has been
1552          * registered as a drag drop item.
1553          * @method isDragDrop
1554          * @param {String} id the element id to check
1555          * @return {boolean} true if this element is a DragDrop item,
1556          * false otherwise
1557          * @static
1558          */
1559         isDragDrop: function(id) {
1560             return ( this.getDDById(id) ) ? true : false;
1561         },
1562
1563         /**
1564          * Returns the drag and drop instances that are in all groups the
1565          * passed in instance belongs to.
1566          * @method getRelated
1567          * @param {DragDrop} p_oDD the obj to get related data for
1568          * @param {boolean} bTargetsOnly if true, only return targetable objs
1569          * @return {DragDrop[]} the related instances
1570          * @static
1571          */
1572         getRelated: function(p_oDD, bTargetsOnly) {
1573             var oDDs = [];
1574             for (var i in p_oDD.groups) {
1575                 for (j in this.ids[i]) {
1576                     var dd = this.ids[i][j];
1577                     if (! this.isTypeOfDD(dd)) {
1578                         continue;
1579                     }
1580                     if (!bTargetsOnly || dd.isTarget) {
1581                         oDDs[oDDs.length] = dd;
1582                     }
1583                 }
1584             }
1585
1586             return oDDs;
1587         },
1588
1589         /**
1590          * Returns true if the specified dd target is a legal target for
1591          * the specifice drag obj
1592          * @method isLegalTarget
1593          * @param {DragDrop} the drag obj
1594          * @param {DragDrop} the target
1595          * @return {boolean} true if the target is a legal target for the
1596          * dd obj
1597          * @static
1598          */
1599         isLegalTarget: function (oDD, oTargetDD) {
1600             var targets = this.getRelated(oDD, true);
1601             for (var i=0, len=targets.length;i<len;++i) {
1602                 if (targets[i].id == oTargetDD.id) {
1603                     return true;
1604                 }
1605             }
1606
1607             return false;
1608         },
1609
1610         /**
1611          * My goal is to be able to transparently determine if an object is
1612          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1613          * returns "object", oDD.constructor.toString() always returns
1614          * "DragDrop" and not the name of the subclass.  So for now it just
1615          * evaluates a well-known variable in DragDrop.
1616          * @method isTypeOfDD
1617          * @param {Object} the object to evaluate
1618          * @return {boolean} true if typeof oDD = DragDrop
1619          * @static
1620          */
1621         isTypeOfDD: function (oDD) {
1622             return (oDD && oDD.__ygDragDrop);
1623         },
1624
1625         /**
1626          * Utility function to determine if a given element has been
1627          * registered as a drag drop handle for the given Drag Drop object.
1628          * @method isHandle
1629          * @param {String} id the element id to check
1630          * @return {boolean} true if this element is a DragDrop handle, false
1631          * otherwise
1632          * @static
1633          */
1634         isHandle: function(sDDId, sHandleId) {
1635             return ( this.handleIds[sDDId] &&
1636                             this.handleIds[sDDId][sHandleId] );
1637         },
1638
1639         /**
1640          * Returns the DragDrop instance for a given id
1641          * @method getDDById
1642          * @param {String} id the id of the DragDrop object
1643          * @return {DragDrop} the drag drop object, null if it is not found
1644          * @static
1645          */
1646         getDDById: function(id) {
1647             for (var i in this.ids) {
1648                 if (this.ids[i][id]) {
1649                     return this.ids[i][id];
1650                 }
1651             }
1652             return null;
1653         },
1654
1655         /**
1656          * Fired after a registered DragDrop object gets the mousedown event.
1657          * Sets up the events required to track the object being dragged
1658          * @method handleMouseDown
1659          * @param {Event} e the event
1660          * @param oDD the DragDrop object being dragged
1661          * @private
1662          * @static
1663          */
1664         handleMouseDown: function(e, oDD) {
1665             if(Roo.QuickTips){
1666                 Roo.QuickTips.disable();
1667             }
1668             this.currentTarget = e.getTarget();
1669
1670             this.dragCurrent = oDD;
1671
1672             var el = oDD.getEl();
1673
1674             // track start position
1675             this.startX = e.getPageX();
1676             this.startY = e.getPageY();
1677
1678             this.deltaX = this.startX - el.offsetLeft;
1679             this.deltaY = this.startY - el.offsetTop;
1680
1681             this.dragThreshMet = false;
1682
1683             this.clickTimeout = setTimeout(
1684                     function() {
1685                         var DDM = Roo.dd.DDM;
1686                         DDM.startDrag(DDM.startX, DDM.startY);
1687                     },
1688                     this.clickTimeThresh );
1689         },
1690
1691         /**
1692          * Fired when either the drag pixel threshol or the mousedown hold
1693          * time threshold has been met.
1694          * @method startDrag
1695          * @param x {int} the X position of the original mousedown
1696          * @param y {int} the Y position of the original mousedown
1697          * @static
1698          */
1699         startDrag: function(x, y) {
1700             clearTimeout(this.clickTimeout);
1701             if (this.dragCurrent) {
1702                 this.dragCurrent.b4StartDrag(x, y);
1703                 this.dragCurrent.startDrag(x, y);
1704             }
1705             this.dragThreshMet = true;
1706         },
1707
1708         /**
1709          * Internal function to handle the mouseup event.  Will be invoked
1710          * from the context of the document.
1711          * @method handleMouseUp
1712          * @param {Event} e the event
1713          * @private
1714          * @static
1715          */
1716         handleMouseUp: function(e) {
1717
1718             if(Roo.QuickTips){
1719                 Roo.QuickTips.enable();
1720             }
1721             if (! this.dragCurrent) {
1722                 return;
1723             }
1724
1725             clearTimeout(this.clickTimeout);
1726
1727             if (this.dragThreshMet) {
1728                 this.fireEvents(e, true);
1729             } else {
1730             }
1731
1732             this.stopDrag(e);
1733
1734             this.stopEvent(e);
1735         },
1736
1737         /**
1738          * Utility to stop event propagation and event default, if these
1739          * features are turned on.
1740          * @method stopEvent
1741          * @param {Event} e the event as returned by this.getEvent()
1742          * @static
1743          */
1744         stopEvent: function(e){
1745             if(this.stopPropagation) {
1746                 e.stopPropagation();
1747             }
1748
1749             if (this.preventDefault) {
1750                 e.preventDefault();
1751             }
1752         },
1753
1754         /**
1755          * Internal function to clean up event handlers after the drag
1756          * operation is complete
1757          * @method stopDrag
1758          * @param {Event} e the event
1759          * @private
1760          * @static
1761          */
1762         stopDrag: function(e) {
1763             // Fire the drag end event for the item that was dragged
1764             if (this.dragCurrent) {
1765                 if (this.dragThreshMet) {
1766                     this.dragCurrent.b4EndDrag(e);
1767                     this.dragCurrent.endDrag(e);
1768                 }
1769
1770                 this.dragCurrent.onMouseUp(e);
1771             }
1772
1773             this.dragCurrent = null;
1774             this.dragOvers = {};
1775         },
1776
1777         /**
1778          * Internal function to handle the mousemove event.  Will be invoked
1779          * from the context of the html element.
1780          *
1781          * @TODO figure out what we can do about mouse events lost when the
1782          * user drags objects beyond the window boundary.  Currently we can
1783          * detect this in internet explorer by verifying that the mouse is
1784          * down during the mousemove event.  Firefox doesn't give us the
1785          * button state on the mousemove event.
1786          * @method handleMouseMove
1787          * @param {Event} e the event
1788          * @private
1789          * @static
1790          */
1791         handleMouseMove: function(e) {
1792             if (! this.dragCurrent) {
1793                 return true;
1794             }
1795
1796             // var button = e.which || e.button;
1797
1798             // check for IE mouseup outside of page boundary
1799             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1800                 this.stopEvent(e);
1801                 return this.handleMouseUp(e);
1802             }
1803
1804             if (!this.dragThreshMet) {
1805                 var diffX = Math.abs(this.startX - e.getPageX());
1806                 var diffY = Math.abs(this.startY - e.getPageY());
1807                 if (diffX > this.clickPixelThresh ||
1808                             diffY > this.clickPixelThresh) {
1809                     this.startDrag(this.startX, this.startY);
1810                 }
1811             }
1812
1813             if (this.dragThreshMet) {
1814                 this.dragCurrent.b4Drag(e);
1815                 this.dragCurrent.onDrag(e);
1816                 if(!this.dragCurrent.moveOnly){
1817                     this.fireEvents(e, false);
1818                 }
1819             }
1820
1821             this.stopEvent(e);
1822
1823             return true;
1824         },
1825
1826         /**
1827          * Iterates over all of the DragDrop elements to find ones we are
1828          * hovering over or dropping on
1829          * @method fireEvents
1830          * @param {Event} e the event
1831          * @param {boolean} isDrop is this a drop op or a mouseover op?
1832          * @private
1833          * @static
1834          */
1835         fireEvents: function(e, isDrop) {
1836             var dc = this.dragCurrent;
1837
1838             // If the user did the mouse up outside of the window, we could
1839             // get here even though we have ended the drag.
1840             if (!dc || dc.isLocked()) {
1841                 return;
1842             }
1843
1844             var pt = e.getPoint();
1845
1846             // cache the previous dragOver array
1847             var oldOvers = [];
1848
1849             var outEvts   = [];
1850             var overEvts  = [];
1851             var dropEvts  = [];
1852             var enterEvts = [];
1853
1854             // Check to see if the object(s) we were hovering over is no longer
1855             // being hovered over so we can fire the onDragOut event
1856             for (var i in this.dragOvers) {
1857
1858                 var ddo = this.dragOvers[i];
1859
1860                 if (! this.isTypeOfDD(ddo)) {
1861                     continue;
1862                 }
1863
1864                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1865                     outEvts.push( ddo );
1866                 }
1867
1868                 oldOvers[i] = true;
1869                 delete this.dragOvers[i];
1870             }
1871
1872             for (var sGroup in dc.groups) {
1873
1874                 if ("string" != typeof sGroup) {
1875                     continue;
1876                 }
1877
1878                 for (i in this.ids[sGroup]) {
1879                     var oDD = this.ids[sGroup][i];
1880                     if (! this.isTypeOfDD(oDD)) {
1881                         continue;
1882                     }
1883
1884                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1885                         if (this.isOverTarget(pt, oDD, this.mode)) {
1886                             // look for drop interactions
1887                             if (isDrop) {
1888                                 dropEvts.push( oDD );
1889                             // look for drag enter and drag over interactions
1890                             } else {
1891
1892                                 // initial drag over: dragEnter fires
1893                                 if (!oldOvers[oDD.id]) {
1894                                     enterEvts.push( oDD );
1895                                 // subsequent drag overs: dragOver fires
1896                                 } else {
1897                                     overEvts.push( oDD );
1898                                 }
1899
1900                                 this.dragOvers[oDD.id] = oDD;
1901                             }
1902                         }
1903                     }
1904                 }
1905             }
1906
1907             if (this.mode) {
1908                 if (outEvts.length) {
1909                     dc.b4DragOut(e, outEvts);
1910                     dc.onDragOut(e, outEvts);
1911                 }
1912
1913                 if (enterEvts.length) {
1914                     dc.onDragEnter(e, enterEvts);
1915                 }
1916
1917                 if (overEvts.length) {
1918                     dc.b4DragOver(e, overEvts);
1919                     dc.onDragOver(e, overEvts);
1920                 }
1921
1922                 if (dropEvts.length) {
1923                     dc.b4DragDrop(e, dropEvts);
1924                     dc.onDragDrop(e, dropEvts);
1925                 }
1926
1927             } else {
1928                 // fire dragout events
1929                 var len = 0;
1930                 for (i=0, len=outEvts.length; i<len; ++i) {
1931                     dc.b4DragOut(e, outEvts[i].id);
1932                     dc.onDragOut(e, outEvts[i].id);
1933                 }
1934
1935                 // fire enter events
1936                 for (i=0,len=enterEvts.length; i<len; ++i) {
1937                     // dc.b4DragEnter(e, oDD.id);
1938                     dc.onDragEnter(e, enterEvts[i].id);
1939                 }
1940
1941                 // fire over events
1942                 for (i=0,len=overEvts.length; i<len; ++i) {
1943                     dc.b4DragOver(e, overEvts[i].id);
1944                     dc.onDragOver(e, overEvts[i].id);
1945                 }
1946
1947                 // fire drop events
1948                 for (i=0, len=dropEvts.length; i<len; ++i) {
1949                     dc.b4DragDrop(e, dropEvts[i].id);
1950                     dc.onDragDrop(e, dropEvts[i].id);
1951                 }
1952
1953             }
1954
1955             // notify about a drop that did not find a target
1956             if (isDrop && !dropEvts.length) {
1957                 dc.onInvalidDrop(e);
1958             }
1959
1960         },
1961
1962         /**
1963          * Helper function for getting the best match from the list of drag
1964          * and drop objects returned by the drag and drop events when we are
1965          * in INTERSECT mode.  It returns either the first object that the
1966          * cursor is over, or the object that has the greatest overlap with
1967          * the dragged element.
1968          * @method getBestMatch
1969          * @param  {DragDrop[]} dds The array of drag and drop objects
1970          * targeted
1971          * @return {DragDrop}       The best single match
1972          * @static
1973          */
1974         getBestMatch: function(dds) {
1975             var winner = null;
1976             // Return null if the input is not what we expect
1977             //if (!dds || !dds.length || dds.length == 0) {
1978                // winner = null;
1979             // If there is only one item, it wins
1980             //} else if (dds.length == 1) {
1981
1982             var len = dds.length;
1983
1984             if (len == 1) {
1985                 winner = dds[0];
1986             } else {
1987                 // Loop through the targeted items
1988                 for (var i=0; i<len; ++i) {
1989                     var dd = dds[i];
1990                     // If the cursor is over the object, it wins.  If the
1991                     // cursor is over multiple matches, the first one we come
1992                     // to wins.
1993                     if (dd.cursorIsOver) {
1994                         winner = dd;
1995                         break;
1996                     // Otherwise the object with the most overlap wins
1997                     } else {
1998                         if (!winner ||
1999                             winner.overlap.getArea() < dd.overlap.getArea()) {
2000                             winner = dd;
2001                         }
2002                     }
2003                 }
2004             }
2005
2006             return winner;
2007         },
2008
2009         /**
2010          * Refreshes the cache of the top-left and bottom-right points of the
2011          * drag and drop objects in the specified group(s).  This is in the
2012          * format that is stored in the drag and drop instance, so typical
2013          * usage is:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2016          * </code>
2017          * Alternatively:
2018          * <code>
2019          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2020          * </code>
2021          * @TODO this really should be an indexed array.  Alternatively this
2022          * method could accept both.
2023          * @method refreshCache
2024          * @param {Object} groups an associative array of groups to refresh
2025          * @static
2026          */
2027         refreshCache: function(groups) {
2028             for (var sGroup in groups) {
2029                 if ("string" != typeof sGroup) {
2030                     continue;
2031                 }
2032                 for (var i in this.ids[sGroup]) {
2033                     var oDD = this.ids[sGroup][i];
2034
2035                     if (this.isTypeOfDD(oDD)) {
2036                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2037                         var loc = this.getLocation(oDD);
2038                         if (loc) {
2039                             this.locationCache[oDD.id] = loc;
2040                         } else {
2041                             delete this.locationCache[oDD.id];
2042                             // this will unregister the drag and drop object if
2043                             // the element is not in a usable state
2044                             // oDD.unreg();
2045                         }
2046                     }
2047                 }
2048             }
2049         },
2050
2051         /**
2052          * This checks to make sure an element exists and is in the DOM.  The
2053          * main purpose is to handle cases where innerHTML is used to remove
2054          * drag and drop objects from the DOM.  IE provides an 'unspecified
2055          * error' when trying to access the offsetParent of such an element
2056          * @method verifyEl
2057          * @param {HTMLElement} el the element to check
2058          * @return {boolean} true if the element looks usable
2059          * @static
2060          */
2061         verifyEl: function(el) {
2062             if (el) {
2063                 var parent;
2064                 if(Roo.isIE){
2065                     try{
2066                         parent = el.offsetParent;
2067                     }catch(e){}
2068                 }else{
2069                     parent = el.offsetParent;
2070                 }
2071                 if (parent) {
2072                     return true;
2073                 }
2074             }
2075
2076             return false;
2077         },
2078
2079         /**
2080          * Returns a Region object containing the drag and drop element's position
2081          * and size, including the padding configured for it
2082          * @method getLocation
2083          * @param {DragDrop} oDD the drag and drop object to get the
2084          *                       location for
2085          * @return {Roo.lib.Region} a Region object representing the total area
2086          *                             the element occupies, including any padding
2087          *                             the instance is configured for.
2088          * @static
2089          */
2090         getLocation: function(oDD) {
2091             if (! this.isTypeOfDD(oDD)) {
2092                 return null;
2093             }
2094
2095             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2096
2097             try {
2098                 pos= Roo.lib.Dom.getXY(el);
2099             } catch (e) { }
2100
2101             if (!pos) {
2102                 return null;
2103             }
2104
2105             x1 = pos[0];
2106             x2 = x1 + el.offsetWidth;
2107             y1 = pos[1];
2108             y2 = y1 + el.offsetHeight;
2109
2110             t = y1 - oDD.padding[0];
2111             r = x2 + oDD.padding[1];
2112             b = y2 + oDD.padding[2];
2113             l = x1 - oDD.padding[3];
2114
2115             return new Roo.lib.Region( t, r, b, l );
2116         },
2117
2118         /**
2119          * Checks the cursor location to see if it over the target
2120          * @method isOverTarget
2121          * @param {Roo.lib.Point} pt The point to evaluate
2122          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2123          * @return {boolean} true if the mouse is over the target
2124          * @private
2125          * @static
2126          */
2127         isOverTarget: function(pt, oTarget, intersect) {
2128             // use cache if available
2129             var loc = this.locationCache[oTarget.id];
2130             if (!loc || !this.useCache) {
2131                 loc = this.getLocation(oTarget);
2132                 this.locationCache[oTarget.id] = loc;
2133
2134             }
2135
2136             if (!loc) {
2137                 return false;
2138             }
2139
2140             oTarget.cursorIsOver = loc.contains( pt );
2141
2142             // DragDrop is using this as a sanity check for the initial mousedown
2143             // in this case we are done.  In POINT mode, if the drag obj has no
2144             // contraints, we are also done. Otherwise we need to evaluate the
2145             // location of the target as related to the actual location of the
2146             // dragged element.
2147             var dc = this.dragCurrent;
2148             if (!dc || !dc.getTargetCoord ||
2149                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2150                 return oTarget.cursorIsOver;
2151             }
2152
2153             oTarget.overlap = null;
2154
2155             // Get the current location of the drag element, this is the
2156             // location of the mouse event less the delta that represents
2157             // where the original mousedown happened on the element.  We
2158             // need to consider constraints and ticks as well.
2159             var pos = dc.getTargetCoord(pt.x, pt.y);
2160
2161             var el = dc.getDragEl();
2162             var curRegion = new Roo.lib.Region( pos.y,
2163                                                    pos.x + el.offsetWidth,
2164                                                    pos.y + el.offsetHeight,
2165                                                    pos.x );
2166
2167             var overlap = curRegion.intersect(loc);
2168
2169             if (overlap) {
2170                 oTarget.overlap = overlap;
2171                 return (intersect) ? true : oTarget.cursorIsOver;
2172             } else {
2173                 return false;
2174             }
2175         },
2176
2177         /**
2178          * unload event handler
2179          * @method _onUnload
2180          * @private
2181          * @static
2182          */
2183         _onUnload: function(e, me) {
2184             Roo.dd.DragDropMgr.unregAll();
2185         },
2186
2187         /**
2188          * Cleans up the drag and drop events and objects.
2189          * @method unregAll
2190          * @private
2191          * @static
2192          */
2193         unregAll: function() {
2194
2195             if (this.dragCurrent) {
2196                 this.stopDrag();
2197                 this.dragCurrent = null;
2198             }
2199
2200             this._execOnAll("unreg", []);
2201
2202             for (i in this.elementCache) {
2203                 delete this.elementCache[i];
2204             }
2205
2206             this.elementCache = {};
2207             this.ids = {};
2208         },
2209
2210         /**
2211          * A cache of DOM elements
2212          * @property elementCache
2213          * @private
2214          * @static
2215          */
2216         elementCache: {},
2217
2218         /**
2219          * Get the wrapper for the DOM element specified
2220          * @method getElWrapper
2221          * @param {String} id the id of the element to get
2222          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2223          * @private
2224          * @deprecated This wrapper isn't that useful
2225          * @static
2226          */
2227         getElWrapper: function(id) {
2228             var oWrapper = this.elementCache[id];
2229             if (!oWrapper || !oWrapper.el) {
2230                 oWrapper = this.elementCache[id] =
2231                     new this.ElementWrapper(Roo.getDom(id));
2232             }
2233             return oWrapper;
2234         },
2235
2236         /**
2237          * Returns the actual DOM element
2238          * @method getElement
2239          * @param {String} id the id of the elment to get
2240          * @return {Object} The element
2241          * @deprecated use Roo.getDom instead
2242          * @static
2243          */
2244         getElement: function(id) {
2245             return Roo.getDom(id);
2246         },
2247
2248         /**
2249          * Returns the style property for the DOM element (i.e.,
2250          * document.getElById(id).style)
2251          * @method getCss
2252          * @param {String} id the id of the elment to get
2253          * @return {Object} The style property of the element
2254          * @deprecated use Roo.getDom instead
2255          * @static
2256          */
2257         getCss: function(id) {
2258             var el = Roo.getDom(id);
2259             return (el) ? el.style : null;
2260         },
2261
2262         /**
2263          * Inner class for cached elements
2264          * @class DragDropMgr.ElementWrapper
2265          * @for DragDropMgr
2266          * @private
2267          * @deprecated
2268          */
2269         ElementWrapper: function(el) {
2270                 /**
2271                  * The element
2272                  * @property el
2273                  */
2274                 this.el = el || null;
2275                 /**
2276                  * The element id
2277                  * @property id
2278                  */
2279                 this.id = this.el && el.id;
2280                 /**
2281                  * A reference to the style property
2282                  * @property css
2283                  */
2284                 this.css = this.el && el.style;
2285             },
2286
2287         /**
2288          * Returns the X position of an html element
2289          * @method getPosX
2290          * @param el the element for which to get the position
2291          * @return {int} the X coordinate
2292          * @for DragDropMgr
2293          * @deprecated use Roo.lib.Dom.getX instead
2294          * @static
2295          */
2296         getPosX: function(el) {
2297             return Roo.lib.Dom.getX(el);
2298         },
2299
2300         /**
2301          * Returns the Y position of an html element
2302          * @method getPosY
2303          * @param el the element for which to get the position
2304          * @return {int} the Y coordinate
2305          * @deprecated use Roo.lib.Dom.getY instead
2306          * @static
2307          */
2308         getPosY: function(el) {
2309             return Roo.lib.Dom.getY(el);
2310         },
2311
2312         /**
2313          * Swap two nodes.  In IE, we use the native method, for others we
2314          * emulate the IE behavior
2315          * @method swapNode
2316          * @param n1 the first node to swap
2317          * @param n2 the other node to swap
2318          * @static
2319          */
2320         swapNode: function(n1, n2) {
2321             if (n1.swapNode) {
2322                 n1.swapNode(n2);
2323             } else {
2324                 var p = n2.parentNode;
2325                 var s = n2.nextSibling;
2326
2327                 if (s == n1) {
2328                     p.insertBefore(n1, n2);
2329                 } else if (n2 == n1.nextSibling) {
2330                     p.insertBefore(n2, n1);
2331                 } else {
2332                     n1.parentNode.replaceChild(n2, n1);
2333                     p.insertBefore(n1, s);
2334                 }
2335             }
2336         },
2337
2338         /**
2339          * Returns the current scroll position
2340          * @method getScroll
2341          * @private
2342          * @static
2343          */
2344         getScroll: function () {
2345             var t, l, dde=document.documentElement, db=document.body;
2346             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2347                 t = dde.scrollTop;
2348                 l = dde.scrollLeft;
2349             } else if (db) {
2350                 t = db.scrollTop;
2351                 l = db.scrollLeft;
2352             } else {
2353
2354             }
2355             return { top: t, left: l };
2356         },
2357
2358         /**
2359          * Returns the specified element style property
2360          * @method getStyle
2361          * @param {HTMLElement} el          the element
2362          * @param {string}      styleProp   the style property
2363          * @return {string} The value of the style property
2364          * @deprecated use Roo.lib.Dom.getStyle
2365          * @static
2366          */
2367         getStyle: function(el, styleProp) {
2368             return Roo.fly(el).getStyle(styleProp);
2369         },
2370
2371         /**
2372          * Gets the scrollTop
2373          * @method getScrollTop
2374          * @return {int} the document's scrollTop
2375          * @static
2376          */
2377         getScrollTop: function () { return this.getScroll().top; },
2378
2379         /**
2380          * Gets the scrollLeft
2381          * @method getScrollLeft
2382          * @return {int} the document's scrollTop
2383          * @static
2384          */
2385         getScrollLeft: function () { return this.getScroll().left; },
2386
2387         /**
2388          * Sets the x/y position of an element to the location of the
2389          * target element.
2390          * @method moveToEl
2391          * @param {HTMLElement} moveEl      The element to move
2392          * @param {HTMLElement} targetEl    The position reference element
2393          * @static
2394          */
2395         moveToEl: function (moveEl, targetEl) {
2396             var aCoord = Roo.lib.Dom.getXY(targetEl);
2397             Roo.lib.Dom.setXY(moveEl, aCoord);
2398         },
2399
2400         /**
2401          * Numeric array sort function
2402          * @method numericSort
2403          * @static
2404          */
2405         numericSort: function(a, b) { return (a - b); },
2406
2407         /**
2408          * Internal counter
2409          * @property _timeoutCount
2410          * @private
2411          * @static
2412          */
2413         _timeoutCount: 0,
2414
2415         /**
2416          * Trying to make the load order less important.  Without this we get
2417          * an error if this file is loaded before the Event Utility.
2418          * @method _addListeners
2419          * @private
2420          * @static
2421          */
2422         _addListeners: function() {
2423             var DDM = Roo.dd.DDM;
2424             if ( Roo.lib.Event && document ) {
2425                 DDM._onLoad();
2426             } else {
2427                 if (DDM._timeoutCount > 2000) {
2428                 } else {
2429                     setTimeout(DDM._addListeners, 10);
2430                     if (document && document.body) {
2431                         DDM._timeoutCount += 1;
2432                     }
2433                 }
2434             }
2435         },
2436
2437         /**
2438          * Recursively searches the immediate parent and all child nodes for
2439          * the handle element in order to determine wheter or not it was
2440          * clicked.
2441          * @method handleWasClicked
2442          * @param node the html element to inspect
2443          * @static
2444          */
2445         handleWasClicked: function(node, id) {
2446             if (this.isHandle(id, node.id)) {
2447                 return true;
2448             } else {
2449                 // check to see if this is a text node child of the one we want
2450                 var p = node.parentNode;
2451
2452                 while (p) {
2453                     if (this.isHandle(id, p.id)) {
2454                         return true;
2455                     } else {
2456                         p = p.parentNode;
2457                     }
2458                 }
2459             }
2460
2461             return false;
2462         }
2463
2464     };
2465
2466 }();
2467
2468 // shorter alias, save a few bytes
2469 Roo.dd.DDM = Roo.dd.DragDropMgr;
2470 Roo.dd.DDM._addListeners();
2471
2472 }/*
2473  * Based on:
2474  * Ext JS Library 1.1.1
2475  * Copyright(c) 2006-2007, Ext JS, LLC.
2476  *
2477  * Originally Released Under LGPL - original licence link has changed is not relivant.
2478  *
2479  * Fork - LGPL
2480  * <script type="text/javascript">
2481  */
2482
2483 /**
2484  * @class Roo.dd.DD
2485  * A DragDrop implementation where the linked element follows the
2486  * mouse cursor during a drag.
2487  * @extends Roo.dd.DragDrop
2488  * @constructor
2489  * @param {String} id the id of the linked element
2490  * @param {String} sGroup the group of related DragDrop items
2491  * @param {object} config an object containing configurable attributes
2492  *                Valid properties for DD:
2493  *                    scroll
2494  */
2495 Roo.dd.DD = function(id, sGroup, config) {
2496     if (id) {
2497         this.init(id, sGroup, config);
2498     }
2499 };
2500
2501 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2502
2503     /**
2504      * When set to true, the utility automatically tries to scroll the browser
2505      * window wehn a drag and drop element is dragged near the viewport boundary.
2506      * Defaults to true.
2507      * @property scroll
2508      * @type boolean
2509      */
2510     scroll: true,
2511
2512     /**
2513      * Sets the pointer offset to the distance between the linked element's top
2514      * left corner and the location the element was clicked
2515      * @method autoOffset
2516      * @param {int} iPageX the X coordinate of the click
2517      * @param {int} iPageY the Y coordinate of the click
2518      */
2519     autoOffset: function(iPageX, iPageY) {
2520         var x = iPageX - this.startPageX;
2521         var y = iPageY - this.startPageY;
2522         this.setDelta(x, y);
2523     },
2524
2525     /**
2526      * Sets the pointer offset.  You can call this directly to force the
2527      * offset to be in a particular location (e.g., pass in 0,0 to set it
2528      * to the center of the object)
2529      * @method setDelta
2530      * @param {int} iDeltaX the distance from the left
2531      * @param {int} iDeltaY the distance from the top
2532      */
2533     setDelta: function(iDeltaX, iDeltaY) {
2534         this.deltaX = iDeltaX;
2535         this.deltaY = iDeltaY;
2536     },
2537
2538     /**
2539      * Sets the drag element to the location of the mousedown or click event,
2540      * maintaining the cursor location relative to the location on the element
2541      * that was clicked.  Override this if you want to place the element in a
2542      * location other than where the cursor is.
2543      * @method setDragElPos
2544      * @param {int} iPageX the X coordinate of the mousedown or drag event
2545      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2546      */
2547     setDragElPos: function(iPageX, iPageY) {
2548         // the first time we do this, we are going to check to make sure
2549         // the element has css positioning
2550
2551         var el = this.getDragEl();
2552         this.alignElWithMouse(el, iPageX, iPageY);
2553     },
2554
2555     /**
2556      * Sets the element to the location of the mousedown or click event,
2557      * maintaining the cursor location relative to the location on the element
2558      * that was clicked.  Override this if you want to place the element in a
2559      * location other than where the cursor is.
2560      * @method alignElWithMouse
2561      * @param {HTMLElement} el the element to move
2562      * @param {int} iPageX the X coordinate of the mousedown or drag event
2563      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2564      */
2565     alignElWithMouse: function(el, iPageX, iPageY) {
2566         var oCoord = this.getTargetCoord(iPageX, iPageY);
2567         var fly = el.dom ? el : Roo.fly(el);
2568         if (!this.deltaSetXY) {
2569             var aCoord = [oCoord.x, oCoord.y];
2570             fly.setXY(aCoord);
2571             var newLeft = fly.getLeft(true);
2572             var newTop  = fly.getTop(true);
2573             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2574         } else {
2575             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2576         }
2577
2578         this.cachePosition(oCoord.x, oCoord.y);
2579         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2580         return oCoord;
2581     },
2582
2583     /**
2584      * Saves the most recent position so that we can reset the constraints and
2585      * tick marks on-demand.  We need to know this so that we can calculate the
2586      * number of pixels the element is offset from its original position.
2587      * @method cachePosition
2588      * @param iPageX the current x position (optional, this just makes it so we
2589      * don't have to look it up again)
2590      * @param iPageY the current y position (optional, this just makes it so we
2591      * don't have to look it up again)
2592      */
2593     cachePosition: function(iPageX, iPageY) {
2594         if (iPageX) {
2595             this.lastPageX = iPageX;
2596             this.lastPageY = iPageY;
2597         } else {
2598             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2599             this.lastPageX = aCoord[0];
2600             this.lastPageY = aCoord[1];
2601         }
2602     },
2603
2604     /**
2605      * Auto-scroll the window if the dragged object has been moved beyond the
2606      * visible window boundary.
2607      * @method autoScroll
2608      * @param {int} x the drag element's x position
2609      * @param {int} y the drag element's y position
2610      * @param {int} h the height of the drag element
2611      * @param {int} w the width of the drag element
2612      * @private
2613      */
2614     autoScroll: function(x, y, h, w) {
2615
2616         if (this.scroll) {
2617             // The client height
2618             var clientH = Roo.lib.Dom.getViewWidth();
2619
2620             // The client width
2621             var clientW = Roo.lib.Dom.getViewHeight();
2622
2623             // The amt scrolled down
2624             var st = this.DDM.getScrollTop();
2625
2626             // The amt scrolled right
2627             var sl = this.DDM.getScrollLeft();
2628
2629             // Location of the bottom of the element
2630             var bot = h + y;
2631
2632             // Location of the right of the element
2633             var right = w + x;
2634
2635             // The distance from the cursor to the bottom of the visible area,
2636             // adjusted so that we don't scroll if the cursor is beyond the
2637             // element drag constraints
2638             var toBot = (clientH + st - y - this.deltaY);
2639
2640             // The distance from the cursor to the right of the visible area
2641             var toRight = (clientW + sl - x - this.deltaX);
2642
2643
2644             // How close to the edge the cursor must be before we scroll
2645             // var thresh = (document.all) ? 100 : 40;
2646             var thresh = 40;
2647
2648             // How many pixels to scroll per autoscroll op.  This helps to reduce
2649             // clunky scrolling. IE is more sensitive about this ... it needs this
2650             // value to be higher.
2651             var scrAmt = (document.all) ? 80 : 30;
2652
2653             // Scroll down if we are near the bottom of the visible page and the
2654             // obj extends below the crease
2655             if ( bot > clientH && toBot < thresh ) {
2656                 window.scrollTo(sl, st + scrAmt);
2657             }
2658
2659             // Scroll up if the window is scrolled down and the top of the object
2660             // goes above the top border
2661             if ( y < st && st > 0 && y - st < thresh ) {
2662                 window.scrollTo(sl, st - scrAmt);
2663             }
2664
2665             // Scroll right if the obj is beyond the right border and the cursor is
2666             // near the border.
2667             if ( right > clientW && toRight < thresh ) {
2668                 window.scrollTo(sl + scrAmt, st);
2669             }
2670
2671             // Scroll left if the window has been scrolled to the right and the obj
2672             // extends past the left border
2673             if ( x < sl && sl > 0 && x - sl < thresh ) {
2674                 window.scrollTo(sl - scrAmt, st);
2675             }
2676         }
2677     },
2678
2679     /**
2680      * Finds the location the element should be placed if we want to move
2681      * it to where the mouse location less the click offset would place us.
2682      * @method getTargetCoord
2683      * @param {int} iPageX the X coordinate of the click
2684      * @param {int} iPageY the Y coordinate of the click
2685      * @return an object that contains the coordinates (Object.x and Object.y)
2686      * @private
2687      */
2688     getTargetCoord: function(iPageX, iPageY) {
2689
2690
2691         var x = iPageX - this.deltaX;
2692         var y = iPageY - this.deltaY;
2693
2694         if (this.constrainX) {
2695             if (x < this.minX) { x = this.minX; }
2696             if (x > this.maxX) { x = this.maxX; }
2697         }
2698
2699         if (this.constrainY) {
2700             if (y < this.minY) { y = this.minY; }
2701             if (y > this.maxY) { y = this.maxY; }
2702         }
2703
2704         x = this.getTick(x, this.xTicks);
2705         y = this.getTick(y, this.yTicks);
2706
2707
2708         return {x:x, y:y};
2709     },
2710
2711     /*
2712      * Sets up config options specific to this class. Overrides
2713      * Roo.dd.DragDrop, but all versions of this method through the
2714      * inheritance chain are called
2715      */
2716     applyConfig: function() {
2717         Roo.dd.DD.superclass.applyConfig.call(this);
2718         this.scroll = (this.config.scroll !== false);
2719     },
2720
2721     /*
2722      * Event that fires prior to the onMouseDown event.  Overrides
2723      * Roo.dd.DragDrop.
2724      */
2725     b4MouseDown: function(e) {
2726         // this.resetConstraints();
2727         this.autoOffset(e.getPageX(),
2728                             e.getPageY());
2729     },
2730
2731     /*
2732      * Event that fires prior to the onDrag event.  Overrides
2733      * Roo.dd.DragDrop.
2734      */
2735     b4Drag: function(e) {
2736         this.setDragElPos(e.getPageX(),
2737                             e.getPageY());
2738     },
2739
2740     toString: function() {
2741         return ("DD " + this.id);
2742     }
2743
2744     //////////////////////////////////////////////////////////////////////////
2745     // Debugging ygDragDrop events that can be overridden
2746     //////////////////////////////////////////////////////////////////////////
2747     /*
2748     startDrag: function(x, y) {
2749     },
2750
2751     onDrag: function(e) {
2752     },
2753
2754     onDragEnter: function(e, id) {
2755     },
2756
2757     onDragOver: function(e, id) {
2758     },
2759
2760     onDragOut: function(e, id) {
2761     },
2762
2763     onDragDrop: function(e, id) {
2764     },
2765
2766     endDrag: function(e) {
2767     }
2768
2769     */
2770
2771 });/*
2772  * Based on:
2773  * Ext JS Library 1.1.1
2774  * Copyright(c) 2006-2007, Ext JS, LLC.
2775  *
2776  * Originally Released Under LGPL - original licence link has changed is not relivant.
2777  *
2778  * Fork - LGPL
2779  * <script type="text/javascript">
2780  */
2781
2782 /**
2783  * @class Roo.dd.DDProxy
2784  * A DragDrop implementation that inserts an empty, bordered div into
2785  * the document that follows the cursor during drag operations.  At the time of
2786  * the click, the frame div is resized to the dimensions of the linked html
2787  * element, and moved to the exact location of the linked element.
2788  *
2789  * References to the "frame" element refer to the single proxy element that
2790  * was created to be dragged in place of all DDProxy elements on the
2791  * page.
2792  *
2793  * @extends Roo.dd.DD
2794  * @constructor
2795  * @param {String} id the id of the linked html element
2796  * @param {String} sGroup the group of related DragDrop objects
2797  * @param {object} config an object containing configurable attributes
2798  *                Valid properties for DDProxy in addition to those in DragDrop:
2799  *                   resizeFrame, centerFrame, dragElId
2800  */
2801 Roo.dd.DDProxy = function(id, sGroup, config) {
2802     if (id) {
2803         this.init(id, sGroup, config);
2804         this.initFrame();
2805     }
2806 };
2807
2808 /**
2809  * The default drag frame div id
2810  * @property Roo.dd.DDProxy.dragElId
2811  * @type String
2812  * @static
2813  */
2814 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2815
2816 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2817
2818     /**
2819      * By default we resize the drag frame to be the same size as the element
2820      * we want to drag (this is to get the frame effect).  We can turn it off
2821      * if we want a different behavior.
2822      * @property resizeFrame
2823      * @type boolean
2824      */
2825     resizeFrame: true,
2826
2827     /**
2828      * By default the frame is positioned exactly where the drag element is, so
2829      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2830      * you do not have constraints on the obj is to have the drag frame centered
2831      * around the cursor.  Set centerFrame to true for this effect.
2832      * @property centerFrame
2833      * @type boolean
2834      */
2835     centerFrame: false,
2836
2837     /**
2838      * Creates the proxy element if it does not yet exist
2839      * @method createFrame
2840      */
2841     createFrame: function() {
2842         var self = this;
2843         var body = document.body;
2844
2845         if (!body || !body.firstChild) {
2846             setTimeout( function() { self.createFrame(); }, 50 );
2847             return;
2848         }
2849
2850         var div = this.getDragEl();
2851
2852         if (!div) {
2853             div    = document.createElement("div");
2854             div.id = this.dragElId;
2855             var s  = div.style;
2856
2857             s.position   = "absolute";
2858             s.visibility = "hidden";
2859             s.cursor     = "move";
2860             s.border     = "2px solid #aaa";
2861             s.zIndex     = 999;
2862
2863             // appendChild can blow up IE if invoked prior to the window load event
2864             // while rendering a table.  It is possible there are other scenarios
2865             // that would cause this to happen as well.
2866             body.insertBefore(div, body.firstChild);
2867         }
2868     },
2869
2870     /**
2871      * Initialization for the drag frame element.  Must be called in the
2872      * constructor of all subclasses
2873      * @method initFrame
2874      */
2875     initFrame: function() {
2876         this.createFrame();
2877     },
2878
2879     applyConfig: function() {
2880         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2881
2882         this.resizeFrame = (this.config.resizeFrame !== false);
2883         this.centerFrame = (this.config.centerFrame);
2884         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2885     },
2886
2887     /**
2888      * Resizes the drag frame to the dimensions of the clicked object, positions
2889      * it over the object, and finally displays it
2890      * @method showFrame
2891      * @param {int} iPageX X click position
2892      * @param {int} iPageY Y click position
2893      * @private
2894      */
2895     showFrame: function(iPageX, iPageY) {
2896         var el = this.getEl();
2897         var dragEl = this.getDragEl();
2898         var s = dragEl.style;
2899
2900         this._resizeProxy();
2901
2902         if (this.centerFrame) {
2903             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2904                            Math.round(parseInt(s.height, 10)/2) );
2905         }
2906
2907         this.setDragElPos(iPageX, iPageY);
2908
2909         Roo.fly(dragEl).show();
2910     },
2911
2912     /**
2913      * The proxy is automatically resized to the dimensions of the linked
2914      * element when a drag is initiated, unless resizeFrame is set to false
2915      * @method _resizeProxy
2916      * @private
2917      */
2918     _resizeProxy: function() {
2919         if (this.resizeFrame) {
2920             var el = this.getEl();
2921             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2922         }
2923     },
2924
2925     // overrides Roo.dd.DragDrop
2926     b4MouseDown: function(e) {
2927         var x = e.getPageX();
2928         var y = e.getPageY();
2929         this.autoOffset(x, y);
2930         this.setDragElPos(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4StartDrag: function(x, y) {
2935         // show the drag frame
2936         this.showFrame(x, y);
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     b4EndDrag: function(e) {
2941         Roo.fly(this.getDragEl()).hide();
2942     },
2943
2944     // overrides Roo.dd.DragDrop
2945     // By default we try to move the element to the last location of the frame.
2946     // This is so that the default behavior mirrors that of Roo.dd.DD.
2947     endDrag: function(e) {
2948
2949         var lel = this.getEl();
2950         var del = this.getDragEl();
2951
2952         // Show the drag frame briefly so we can get its position
2953         del.style.visibility = "";
2954
2955         this.beforeMove();
2956         // Hide the linked element before the move to get around a Safari
2957         // rendering bug.
2958         lel.style.visibility = "hidden";
2959         Roo.dd.DDM.moveToEl(lel, del);
2960         del.style.visibility = "hidden";
2961         lel.style.visibility = "";
2962
2963         this.afterDrag();
2964     },
2965
2966     beforeMove : function(){
2967
2968     },
2969
2970     afterDrag : function(){
2971
2972     },
2973
2974     toString: function() {
2975         return ("DDProxy " + this.id);
2976     }
2977
2978 });
2979 /*
2980  * Based on:
2981  * Ext JS Library 1.1.1
2982  * Copyright(c) 2006-2007, Ext JS, LLC.
2983  *
2984  * Originally Released Under LGPL - original licence link has changed is not relivant.
2985  *
2986  * Fork - LGPL
2987  * <script type="text/javascript">
2988  */
2989
2990  /**
2991  * @class Roo.dd.DDTarget
2992  * A DragDrop implementation that does not move, but can be a drop
2993  * target.  You would get the same result by simply omitting implementation
2994  * for the event callbacks, but this way we reduce the processing cost of the
2995  * event listener and the callbacks.
2996  * @extends Roo.dd.DragDrop
2997  * @constructor
2998  * @param {String} id the id of the element that is a drop target
2999  * @param {String} sGroup the group of related DragDrop objects
3000  * @param {object} config an object containing configurable attributes
3001  *                 Valid properties for DDTarget in addition to those in
3002  *                 DragDrop:
3003  *                    none
3004  */
3005 Roo.dd.DDTarget = function(id, sGroup, config) {
3006     if (id) {
3007         this.initTarget(id, sGroup, config);
3008     }
3009 };
3010
3011 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3012 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3013     toString: function() {
3014         return ("DDTarget " + this.id);
3015     }
3016 });
3017 /*
3018  * Based on:
3019  * Ext JS Library 1.1.1
3020  * Copyright(c) 2006-2007, Ext JS, LLC.
3021  *
3022  * Originally Released Under LGPL - original licence link has changed is not relivant.
3023  *
3024  * Fork - LGPL
3025  * <script type="text/javascript">
3026  */
3027  
3028
3029 /**
3030  * @class Roo.dd.ScrollManager
3031  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3032  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3033  * @singleton
3034  */
3035 Roo.dd.ScrollManager = function(){
3036     var ddm = Roo.dd.DragDropMgr;
3037     var els = {};
3038     var dragEl = null;
3039     var proc = {};
3040     
3041     var onStop = function(e){
3042         dragEl = null;
3043         clearProc();
3044     };
3045     
3046     var triggerRefresh = function(){
3047         if(ddm.dragCurrent){
3048              ddm.refreshCache(ddm.dragCurrent.groups);
3049         }
3050     };
3051     
3052     var doScroll = function(){
3053         if(ddm.dragCurrent){
3054             var dds = Roo.dd.ScrollManager;
3055             if(!dds.animate){
3056                 if(proc.el.scroll(proc.dir, dds.increment)){
3057                     triggerRefresh();
3058                 }
3059             }else{
3060                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3061             }
3062         }
3063     };
3064     
3065     var clearProc = function(){
3066         if(proc.id){
3067             clearInterval(proc.id);
3068         }
3069         proc.id = 0;
3070         proc.el = null;
3071         proc.dir = "";
3072     };
3073     
3074     var startProc = function(el, dir){
3075         clearProc();
3076         proc.el = el;
3077         proc.dir = dir;
3078         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3079     };
3080     
3081     var onFire = function(e, isDrop){
3082         if(isDrop || !ddm.dragCurrent){ return; }
3083         var dds = Roo.dd.ScrollManager;
3084         if(!dragEl || dragEl != ddm.dragCurrent){
3085             dragEl = ddm.dragCurrent;
3086             // refresh regions on drag start
3087             dds.refreshCache();
3088         }
3089         
3090         var xy = Roo.lib.Event.getXY(e);
3091         var pt = new Roo.lib.Point(xy[0], xy[1]);
3092         for(var id in els){
3093             var el = els[id], r = el._region;
3094             if(r && r.contains(pt) && el.isScrollable()){
3095                 if(r.bottom - pt.y <= dds.thresh){
3096                     if(proc.el != el){
3097                         startProc(el, "down");
3098                     }
3099                     return;
3100                 }else if(r.right - pt.x <= dds.thresh){
3101                     if(proc.el != el){
3102                         startProc(el, "left");
3103                     }
3104                     return;
3105                 }else if(pt.y - r.top <= dds.thresh){
3106                     if(proc.el != el){
3107                         startProc(el, "up");
3108                     }
3109                     return;
3110                 }else if(pt.x - r.left <= dds.thresh){
3111                     if(proc.el != el){
3112                         startProc(el, "right");
3113                     }
3114                     return;
3115                 }
3116             }
3117         }
3118         clearProc();
3119     };
3120     
3121     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3122     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3123     
3124     return {
3125         /**
3126          * Registers new overflow element(s) to auto scroll
3127          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3128          */
3129         register : function(el){
3130             if(el instanceof Array){
3131                 for(var i = 0, len = el.length; i < len; i++) {
3132                         this.register(el[i]);
3133                 }
3134             }else{
3135                 el = Roo.get(el);
3136                 els[el.id] = el;
3137             }
3138         },
3139         
3140         /**
3141          * Unregisters overflow element(s) so they are no longer scrolled
3142          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3143          */
3144         unregister : function(el){
3145             if(el instanceof Array){
3146                 for(var i = 0, len = el.length; i < len; i++) {
3147                         this.unregister(el[i]);
3148                 }
3149             }else{
3150                 el = Roo.get(el);
3151                 delete els[el.id];
3152             }
3153         },
3154         
3155         /**
3156          * The number of pixels from the edge of a container the pointer needs to be to 
3157          * trigger scrolling (defaults to 25)
3158          * @type Number
3159          */
3160         thresh : 25,
3161         
3162         /**
3163          * The number of pixels to scroll in each scroll increment (defaults to 50)
3164          * @type Number
3165          */
3166         increment : 100,
3167         
3168         /**
3169          * The frequency of scrolls in milliseconds (defaults to 500)
3170          * @type Number
3171          */
3172         frequency : 500,
3173         
3174         /**
3175          * True to animate the scroll (defaults to true)
3176          * @type Boolean
3177          */
3178         animate: true,
3179         
3180         /**
3181          * The animation duration in seconds - 
3182          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3183          * @type Number
3184          */
3185         animDuration: .4,
3186         
3187         /**
3188          * Manually trigger a cache refresh.
3189          */
3190         refreshCache : function(){
3191             for(var id in els){
3192                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3193                     els[id]._region = els[id].getRegion();
3194                 }
3195             }
3196         }
3197     };
3198 }();/*
3199  * Based on:
3200  * Ext JS Library 1.1.1
3201  * Copyright(c) 2006-2007, Ext JS, LLC.
3202  *
3203  * Originally Released Under LGPL - original licence link has changed is not relivant.
3204  *
3205  * Fork - LGPL
3206  * <script type="text/javascript">
3207  */
3208  
3209
3210 /**
3211  * @class Roo.dd.Registry
3212  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3213  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3214  * @singleton
3215  */
3216 Roo.dd.Registry = function(){
3217     var elements = {}; 
3218     var handles = {}; 
3219     var autoIdSeed = 0;
3220
3221     var getId = function(el, autogen){
3222         if(typeof el == "string"){
3223             return el;
3224         }
3225         var id = el.id;
3226         if(!id && autogen !== false){
3227             id = "roodd-" + (++autoIdSeed);
3228             el.id = id;
3229         }
3230         return id;
3231     };
3232     
3233     return {
3234     /**
3235      * Register a drag drop element
3236      * @param {String|HTMLElement} element The id or DOM node to register
3237      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3238      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3239      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3240      * populated in the data object (if applicable):
3241      * <pre>
3242 Value      Description<br />
3243 ---------  ------------------------------------------<br />
3244 handles    Array of DOM nodes that trigger dragging<br />
3245            for the element being registered<br />
3246 isHandle   True if the element passed in triggers<br />
3247            dragging itself, else false
3248 </pre>
3249      */
3250         register : function(el, data){
3251             data = data || {};
3252             if(typeof el == "string"){
3253                 el = document.getElementById(el);
3254             }
3255             data.ddel = el;
3256             elements[getId(el)] = data;
3257             if(data.isHandle !== false){
3258                 handles[data.ddel.id] = data;
3259             }
3260             if(data.handles){
3261                 var hs = data.handles;
3262                 for(var i = 0, len = hs.length; i < len; i++){
3263                         handles[getId(hs[i])] = data;
3264                 }
3265             }
3266         },
3267
3268     /**
3269      * Unregister a drag drop element
3270      * @param {String|HTMLElement}  element The id or DOM node to unregister
3271      */
3272         unregister : function(el){
3273             var id = getId(el, false);
3274             var data = elements[id];
3275             if(data){
3276                 delete elements[id];
3277                 if(data.handles){
3278                     var hs = data.handles;
3279                     for(var i = 0, len = hs.length; i < len; i++){
3280                         delete handles[getId(hs[i], false)];
3281                     }
3282                 }
3283             }
3284         },
3285
3286     /**
3287      * Returns the handle registered for a DOM Node by id
3288      * @param {String|HTMLElement} id The DOM node or id to look up
3289      * @return {Object} handle The custom handle data
3290      */
3291         getHandle : function(id){
3292             if(typeof id != "string"){ // must be element?
3293                 id = id.id;
3294             }
3295             return handles[id];
3296         },
3297
3298     /**
3299      * Returns the handle that is registered for the DOM node that is the target of the event
3300      * @param {Event} e The event
3301      * @return {Object} handle The custom handle data
3302      */
3303         getHandleFromEvent : function(e){
3304             var t = Roo.lib.Event.getTarget(e);
3305             return t ? handles[t.id] : null;
3306         },
3307
3308     /**
3309      * Returns a custom data object that is registered for a DOM node by id
3310      * @param {String|HTMLElement} id The DOM node or id to look up
3311      * @return {Object} data The custom data
3312      */
3313         getTarget : function(id){
3314             if(typeof id != "string"){ // must be element?
3315                 id = id.id;
3316             }
3317             return elements[id];
3318         },
3319
3320     /**
3321      * Returns a custom data object that is registered for the DOM node that is the target of the event
3322      * @param {Event} e The event
3323      * @return {Object} data The custom data
3324      */
3325         getTargetFromEvent : function(e){
3326             var t = Roo.lib.Event.getTarget(e);
3327             return t ? elements[t.id] || handles[t.id] : null;
3328         }
3329     };
3330 }();/*
3331  * Based on:
3332  * Ext JS Library 1.1.1
3333  * Copyright(c) 2006-2007, Ext JS, LLC.
3334  *
3335  * Originally Released Under LGPL - original licence link has changed is not relivant.
3336  *
3337  * Fork - LGPL
3338  * <script type="text/javascript">
3339  */
3340  
3341
3342 /**
3343  * @class Roo.dd.StatusProxy
3344  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3345  * default drag proxy used by all Roo.dd components.
3346  * @constructor
3347  * @param {Object} config
3348  */
3349 Roo.dd.StatusProxy = function(config){
3350     Roo.apply(this, config);
3351     this.id = this.id || Roo.id();
3352     this.el = new Roo.Layer({
3353         dh: {
3354             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3355                 {tag: "div", cls: "x-dd-drop-icon"},
3356                 {tag: "div", cls: "x-dd-drag-ghost"}
3357             ]
3358         }, 
3359         shadow: !config || config.shadow !== false
3360     });
3361     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3362     this.dropStatus = this.dropNotAllowed;
3363 };
3364
3365 Roo.dd.StatusProxy.prototype = {
3366     /**
3367      * @cfg {String} dropAllowed
3368      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3369      */
3370     dropAllowed : "x-dd-drop-ok",
3371     /**
3372      * @cfg {String} dropNotAllowed
3373      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3374      */
3375     dropNotAllowed : "x-dd-drop-nodrop",
3376
3377     /**
3378      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3379      * over the current target element.
3380      * @param {String} cssClass The css class for the new drop status indicator image
3381      */
3382     setStatus : function(cssClass){
3383         cssClass = cssClass || this.dropNotAllowed;
3384         if(this.dropStatus != cssClass){
3385             this.el.replaceClass(this.dropStatus, cssClass);
3386             this.dropStatus = cssClass;
3387         }
3388     },
3389
3390     /**
3391      * Resets the status indicator to the default dropNotAllowed value
3392      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3393      */
3394     reset : function(clearGhost){
3395         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3396         this.dropStatus = this.dropNotAllowed;
3397         if(clearGhost){
3398             this.ghost.update("");
3399         }
3400     },
3401
3402     /**
3403      * Updates the contents of the ghost element
3404      * @param {String} html The html that will replace the current innerHTML of the ghost element
3405      */
3406     update : function(html){
3407         if(typeof html == "string"){
3408             this.ghost.update(html);
3409         }else{
3410             this.ghost.update("");
3411             html.style.margin = "0";
3412             this.ghost.dom.appendChild(html);
3413         }
3414         // ensure float = none set?? cant remember why though.
3415         var el = this.ghost.dom.firstChild;
3416                 if(el){
3417                         Roo.fly(el).setStyle('float', 'none');
3418                 }
3419     },
3420     
3421     /**
3422      * Returns the underlying proxy {@link Roo.Layer}
3423      * @return {Roo.Layer} el
3424     */
3425     getEl : function(){
3426         return this.el;
3427     },
3428
3429     /**
3430      * Returns the ghost element
3431      * @return {Roo.Element} el
3432      */
3433     getGhost : function(){
3434         return this.ghost;
3435     },
3436
3437     /**
3438      * Hides the proxy
3439      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3440      */
3441     hide : function(clear){
3442         this.el.hide();
3443         if(clear){
3444             this.reset(true);
3445         }
3446     },
3447
3448     /**
3449      * Stops the repair animation if it's currently running
3450      */
3451     stop : function(){
3452         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3453             this.anim.stop();
3454         }
3455     },
3456
3457     /**
3458      * Displays this proxy
3459      */
3460     show : function(){
3461         this.el.show();
3462     },
3463
3464     /**
3465      * Force the Layer to sync its shadow and shim positions to the element
3466      */
3467     sync : function(){
3468         this.el.sync();
3469     },
3470
3471     /**
3472      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3473      * invalid drop operation by the item being dragged.
3474      * @param {Array} xy The XY position of the element ([x, y])
3475      * @param {Function} callback The function to call after the repair is complete
3476      * @param {Object} scope The scope in which to execute the callback
3477      */
3478     repair : function(xy, callback, scope){
3479         this.callback = callback;
3480         this.scope = scope;
3481         if(xy && this.animRepair !== false){
3482             this.el.addClass("x-dd-drag-repair");
3483             this.el.hideUnders(true);
3484             this.anim = this.el.shift({
3485                 duration: this.repairDuration || .5,
3486                 easing: 'easeOut',
3487                 xy: xy,
3488                 stopFx: true,
3489                 callback: this.afterRepair,
3490                 scope: this
3491             });
3492         }else{
3493             this.afterRepair();
3494         }
3495     },
3496
3497     // private
3498     afterRepair : function(){
3499         this.hide(true);
3500         if(typeof this.callback == "function"){
3501             this.callback.call(this.scope || this);
3502         }
3503         this.callback = null;
3504         this.scope = null;
3505     }
3506 };/*
3507  * Based on:
3508  * Ext JS Library 1.1.1
3509  * Copyright(c) 2006-2007, Ext JS, LLC.
3510  *
3511  * Originally Released Under LGPL - original licence link has changed is not relivant.
3512  *
3513  * Fork - LGPL
3514  * <script type="text/javascript">
3515  */
3516
3517 /**
3518  * @class Roo.dd.DragSource
3519  * @extends Roo.dd.DDProxy
3520  * A simple class that provides the basic implementation needed to make any element draggable.
3521  * @constructor
3522  * @param {String/HTMLElement/Element} el The container element
3523  * @param {Object} config
3524  */
3525 Roo.dd.DragSource = function(el, config){
3526     this.el = Roo.get(el);
3527     this.dragData = {};
3528     
3529     Roo.apply(this, config);
3530     
3531     if(!this.proxy){
3532         this.proxy = new Roo.dd.StatusProxy();
3533     }
3534
3535     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3536           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3537     
3538     this.dragging = false;
3539 };
3540
3541 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3542     /**
3543      * @cfg {String} dropAllowed
3544      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3545      */
3546     dropAllowed : "x-dd-drop-ok",
3547     /**
3548      * @cfg {String} dropNotAllowed
3549      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3550      */
3551     dropNotAllowed : "x-dd-drop-nodrop",
3552
3553     /**
3554      * Returns the data object associated with this drag source
3555      * @return {Object} data An object containing arbitrary data
3556      */
3557     getDragData : function(e){
3558         return this.dragData;
3559     },
3560
3561     // private
3562     onDragEnter : function(e, id){
3563         var target = Roo.dd.DragDropMgr.getDDById(id);
3564         this.cachedTarget = target;
3565         if(this.beforeDragEnter(target, e, id) !== false){
3566             if(target.isNotifyTarget){
3567                 var status = target.notifyEnter(this, e, this.dragData);
3568                 this.proxy.setStatus(status);
3569             }else{
3570                 this.proxy.setStatus(this.dropAllowed);
3571             }
3572             
3573             if(this.afterDragEnter){
3574                 /**
3575                  * An empty function by default, but provided so that you can perform a custom action
3576                  * when the dragged item enters the drop target by providing an implementation.
3577                  * @param {Roo.dd.DragDrop} target The drop target
3578                  * @param {Event} e The event object
3579                  * @param {String} id The id of the dragged element
3580                  * @method afterDragEnter
3581                  */
3582                 this.afterDragEnter(target, e, id);
3583             }
3584         }
3585     },
3586
3587     /**
3588      * An empty function by default, but provided so that you can perform a custom action
3589      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3590      * @param {Roo.dd.DragDrop} target The drop target
3591      * @param {Event} e The event object
3592      * @param {String} id The id of the dragged element
3593      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3594      */
3595     beforeDragEnter : function(target, e, id){
3596         return true;
3597     },
3598
3599     // private
3600     alignElWithMouse: function() {
3601         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3602         this.proxy.sync();
3603     },
3604
3605     // private
3606     onDragOver : function(e, id){
3607         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3608         if(this.beforeDragOver(target, e, id) !== false){
3609             if(target.isNotifyTarget){
3610                 var status = target.notifyOver(this, e, this.dragData);
3611                 this.proxy.setStatus(status);
3612             }
3613
3614             if(this.afterDragOver){
3615                 /**
3616                  * An empty function by default, but provided so that you can perform a custom action
3617                  * while the dragged item is over the drop target by providing an implementation.
3618                  * @param {Roo.dd.DragDrop} target The drop target
3619                  * @param {Event} e The event object
3620                  * @param {String} id The id of the dragged element
3621                  * @method afterDragOver
3622                  */
3623                 this.afterDragOver(target, e, id);
3624             }
3625         }
3626     },
3627
3628     /**
3629      * An empty function by default, but provided so that you can perform a custom action
3630      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3631      * @param {Roo.dd.DragDrop} target The drop target
3632      * @param {Event} e The event object
3633      * @param {String} id The id of the dragged element
3634      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3635      */
3636     beforeDragOver : function(target, e, id){
3637         return true;
3638     },
3639
3640     // private
3641     onDragOut : function(e, id){
3642         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3643         if(this.beforeDragOut(target, e, id) !== false){
3644             if(target.isNotifyTarget){
3645                 target.notifyOut(this, e, this.dragData);
3646             }
3647             this.proxy.reset();
3648             if(this.afterDragOut){
3649                 /**
3650                  * An empty function by default, but provided so that you can perform a custom action
3651                  * after the dragged item is dragged out of the target without dropping.
3652                  * @param {Roo.dd.DragDrop} target The drop target
3653                  * @param {Event} e The event object
3654                  * @param {String} id The id of the dragged element
3655                  * @method afterDragOut
3656                  */
3657                 this.afterDragOut(target, e, id);
3658             }
3659         }
3660         this.cachedTarget = null;
3661     },
3662
3663     /**
3664      * An empty function by default, but provided so that you can perform a custom action before the dragged
3665      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3666      * @param {Roo.dd.DragDrop} target The drop target
3667      * @param {Event} e The event object
3668      * @param {String} id The id of the dragged element
3669      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3670      */
3671     beforeDragOut : function(target, e, id){
3672         return true;
3673     },
3674     
3675     // private
3676     onDragDrop : function(e, id){
3677         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3678         if(this.beforeDragDrop(target, e, id) !== false){
3679             if(target.isNotifyTarget){
3680                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3681                     this.onValidDrop(target, e, id);
3682                 }else{
3683                     this.onInvalidDrop(target, e, id);
3684                 }
3685             }else{
3686                 this.onValidDrop(target, e, id);
3687             }
3688             
3689             if(this.afterDragDrop){
3690                 /**
3691                  * An empty function by default, but provided so that you can perform a custom action
3692                  * after a valid drag drop has occurred by providing an implementation.
3693                  * @param {Roo.dd.DragDrop} target The drop target
3694                  * @param {Event} e The event object
3695                  * @param {String} id The id of the dropped element
3696                  * @method afterDragDrop
3697                  */
3698                 this.afterDragDrop(target, e, id);
3699             }
3700         }
3701         delete this.cachedTarget;
3702     },
3703
3704     /**
3705      * An empty function by default, but provided so that you can perform a custom action before the dragged
3706      * item is dropped onto the target and optionally cancel the onDragDrop.
3707      * @param {Roo.dd.DragDrop} target The drop target
3708      * @param {Event} e The event object
3709      * @param {String} id The id of the dragged element
3710      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3711      */
3712     beforeDragDrop : function(target, e, id){
3713         return true;
3714     },
3715
3716     // private
3717     onValidDrop : function(target, e, id){
3718         this.hideProxy();
3719         if(this.afterValidDrop){
3720             /**
3721              * An empty function by default, but provided so that you can perform a custom action
3722              * after a valid drop has occurred by providing an implementation.
3723              * @param {Object} target The target DD 
3724              * @param {Event} e The event object
3725              * @param {String} id The id of the dropped element
3726              * @method afterInvalidDrop
3727              */
3728             this.afterValidDrop(target, e, id);
3729         }
3730     },
3731
3732     // private
3733     getRepairXY : function(e, data){
3734         return this.el.getXY();  
3735     },
3736
3737     // private
3738     onInvalidDrop : function(target, e, id){
3739         this.beforeInvalidDrop(target, e, id);
3740         if(this.cachedTarget){
3741             if(this.cachedTarget.isNotifyTarget){
3742                 this.cachedTarget.notifyOut(this, e, this.dragData);
3743             }
3744             this.cacheTarget = null;
3745         }
3746         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3747
3748         if(this.afterInvalidDrop){
3749             /**
3750              * An empty function by default, but provided so that you can perform a custom action
3751              * after an invalid drop has occurred by providing an implementation.
3752              * @param {Event} e The event object
3753              * @param {String} id The id of the dropped element
3754              * @method afterInvalidDrop
3755              */
3756             this.afterInvalidDrop(e, id);
3757         }
3758     },
3759
3760     // private
3761     afterRepair : function(){
3762         if(Roo.enableFx){
3763             this.el.highlight(this.hlColor || "c3daf9");
3764         }
3765         this.dragging = false;
3766     },
3767
3768     /**
3769      * An empty function by default, but provided so that you can perform a custom action after an invalid
3770      * drop has occurred.
3771      * @param {Roo.dd.DragDrop} target The drop target
3772      * @param {Event} e The event object
3773      * @param {String} id The id of the dragged element
3774      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3775      */
3776     beforeInvalidDrop : function(target, e, id){
3777         return true;
3778     },
3779
3780     // private
3781     handleMouseDown : function(e){
3782         if(this.dragging) {
3783             return;
3784         }
3785         var data = this.getDragData(e);
3786         if(data && this.onBeforeDrag(data, e) !== false){
3787             this.dragData = data;
3788             this.proxy.stop();
3789             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3790         } 
3791     },
3792
3793     /**
3794      * An empty function by default, but provided so that you can perform a custom action before the initial
3795      * drag event begins and optionally cancel it.
3796      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3797      * @param {Event} e The event object
3798      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3799      */
3800     onBeforeDrag : function(data, e){
3801         return true;
3802     },
3803
3804     /**
3805      * An empty function by default, but provided so that you can perform a custom action once the initial
3806      * drag event has begun.  The drag cannot be canceled from this function.
3807      * @param {Number} x The x position of the click on the dragged object
3808      * @param {Number} y The y position of the click on the dragged object
3809      */
3810     onStartDrag : Roo.emptyFn,
3811
3812     // private - YUI override
3813     startDrag : function(x, y){
3814         this.proxy.reset();
3815         this.dragging = true;
3816         this.proxy.update("");
3817         this.onInitDrag(x, y);
3818         this.proxy.show();
3819     },
3820
3821     // private
3822     onInitDrag : function(x, y){
3823         var clone = this.el.dom.cloneNode(true);
3824         clone.id = Roo.id(); // prevent duplicate ids
3825         this.proxy.update(clone);
3826         this.onStartDrag(x, y);
3827         return true;
3828     },
3829
3830     /**
3831      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3832      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3833      */
3834     getProxy : function(){
3835         return this.proxy;  
3836     },
3837
3838     /**
3839      * Hides the drag source's {@link Roo.dd.StatusProxy}
3840      */
3841     hideProxy : function(){
3842         this.proxy.hide();  
3843         this.proxy.reset(true);
3844         this.dragging = false;
3845     },
3846
3847     // private
3848     triggerCacheRefresh : function(){
3849         Roo.dd.DDM.refreshCache(this.groups);
3850     },
3851
3852     // private - override to prevent hiding
3853     b4EndDrag: function(e) {
3854     },
3855
3856     // private - override to prevent moving
3857     endDrag : function(e){
3858         this.onEndDrag(this.dragData, e);
3859     },
3860
3861     // private
3862     onEndDrag : function(data, e){
3863     },
3864     
3865     // private - pin to cursor
3866     autoOffset : function(x, y) {
3867         this.setDelta(-12, -20);
3868     }    
3869 });/*
3870  * Based on:
3871  * Ext JS Library 1.1.1
3872  * Copyright(c) 2006-2007, Ext JS, LLC.
3873  *
3874  * Originally Released Under LGPL - original licence link has changed is not relivant.
3875  *
3876  * Fork - LGPL
3877  * <script type="text/javascript">
3878  */
3879
3880
3881 /**
3882  * @class Roo.dd.DropTarget
3883  * @extends Roo.dd.DDTarget
3884  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3885  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3886  * @constructor
3887  * @param {String/HTMLElement/Element} el The container element
3888  * @param {Object} config
3889  */
3890 Roo.dd.DropTarget = function(el, config){
3891     this.el = Roo.get(el);
3892     
3893     Roo.apply(this, config);
3894     
3895     if(this.containerScroll){
3896         Roo.dd.ScrollManager.register(this.el);
3897     }
3898     
3899      
3900     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3901         this.el.dom, 
3902         this.ddGroup || this.group,
3903         {
3904             isTarget: true,
3905             events : {
3906                  /**
3907                  * @scope Roo.dd.DropTarget
3908                  */
3909                  
3910                  /**
3911                  * @event enter
3912                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913                  * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914                  * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915                  * 
3916                  * IMPORTANT : it should set this.overClass and this.dropAllowed
3917                  * 
3918                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919                  * @param {Event} e The event
3920                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3921                  */
3922                 "enter" : true,
3923                 
3924                  /**
3925                  * @event over
3926                  * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927                  * This method will be called on every mouse movement while the drag source is over the drop target.
3928                  * This default implementation simply returns the dropAllowed config value.
3929                  * 
3930                  * IMPORTANT : it should set this.dropAllowed
3931                  * 
3932                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933                  * @param {Event} e The event
3934                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3935                  
3936                  */
3937                 "over" : true,
3938                 /**
3939                  * @event out
3940                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941                  * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942                  * overClass (if any) from the drop element.
3943                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944                  * @param {Event} e The event
3945                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3946                  */
3947                  "out" : true,
3948                  
3949                 /**
3950                  * @event drop
3951                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952                  * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953                  * implementation that does something to process the drop event and returns true so that the drag source's
3954                  * repair action does not run.
3955                  * 
3956                  * IMPORTANT : it should set this.success
3957                  * 
3958                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959                  * @param {Event} e The event
3960                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3961                 */
3962                  "drop" : true
3963             }
3964                 
3965         
3966         }
3967     );
3968
3969 };
3970
3971 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3972     /**
3973      * @cfg {String} overClass
3974      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3975      */
3976     /**
3977      * @cfg {String} dropAllowed
3978      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3979      */
3980     dropAllowed : "x-dd-drop-ok",
3981     /**
3982      * @cfg {String} dropNotAllowed
3983      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3984      */
3985     dropNotAllowed : "x-dd-drop-nodrop",
3986     /**
3987      * @cfg {boolean} success
3988      * set this after drop listener.. 
3989      */
3990     success : false,
3991     /**
3992      * @cfg {boolean} valid
3993      * if the drop point is valid for over/enter..
3994      */
3995     valid : false,
3996     // private
3997     isTarget : true,
3998
3999     // private
4000     isNotifyTarget : true,
4001     
4002     // private
4003
4004     notifyEnter : function(dd, e, data){
4005         this.valid = true;
4006         this.fireEvent('enter', this, dd, e, data);
4007         if(this.overClass){
4008             this.el.addClass(this.overClass);
4009         }
4010         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4011     },
4012
4013     // private
4014
4015     notifyOver : function(dd, e, data){
4016         this.valid = true;
4017         this.fireEvent('over', this, dd, e, data);
4018         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4019     },
4020
4021     
4022     notifyOut : function(dd, e, data){
4023         this.fireEvent('out', this, dd, e, data);
4024         if(this.overClass){
4025             this.el.removeClass(this.overClass);
4026         }
4027     },
4028
4029     
4030     notifyDrop : function(dd, e, data){
4031         this.success = false;
4032         this.fireEvent('drop', this, dd, e, data);
4033         return this.success;
4034     }
4035 });/*
4036  * Based on:
4037  * Ext JS Library 1.1.1
4038  * Copyright(c) 2006-2007, Ext JS, LLC.
4039  *
4040  * Originally Released Under LGPL - original licence link has changed is not relivant.
4041  *
4042  * Fork - LGPL
4043  * <script type="text/javascript">
4044  */
4045
4046
4047 /**
4048  * @class Roo.dd.DragZone
4049  * @extends Roo.dd.DragSource
4050  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4051  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4052  * @constructor
4053  * @param {String/HTMLElement/Element} el The container element
4054  * @param {Object} config
4055  */
4056 Roo.dd.DragZone = function(el, config){
4057     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4058     if(this.containerScroll){
4059         Roo.dd.ScrollManager.register(this.el);
4060     }
4061 };
4062
4063 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4064     /**
4065      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4066      * for auto scrolling during drag operations.
4067      */
4068     /**
4069      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4070      * method after a failed drop (defaults to "c3daf9" - light blue)
4071      */
4072
4073     /**
4074      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4075      * for a valid target to drag based on the mouse down. Override this method
4076      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4077      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4078      * @param {EventObject} e The mouse down event
4079      * @return {Object} The dragData
4080      */
4081     getDragData : function(e){
4082         return Roo.dd.Registry.getHandleFromEvent(e);
4083     },
4084     
4085     /**
4086      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4087      * this.dragData.ddel
4088      * @param {Number} x The x position of the click on the dragged object
4089      * @param {Number} y The y position of the click on the dragged object
4090      * @return {Boolean} true to continue the drag, false to cancel
4091      */
4092     onInitDrag : function(x, y){
4093         this.proxy.update(this.dragData.ddel.cloneNode(true));
4094         this.onStartDrag(x, y);
4095         return true;
4096     },
4097     
4098     /**
4099      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4100      */
4101     afterRepair : function(){
4102         if(Roo.enableFx){
4103             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4104         }
4105         this.dragging = false;
4106     },
4107
4108     /**
4109      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4110      * the XY of this.dragData.ddel
4111      * @param {EventObject} e The mouse up event
4112      * @return {Array} The xy location (e.g. [100, 200])
4113      */
4114     getRepairXY : function(e){
4115         return Roo.Element.fly(this.dragData.ddel).getXY();  
4116     }
4117 });/*
4118  * Based on:
4119  * Ext JS Library 1.1.1
4120  * Copyright(c) 2006-2007, Ext JS, LLC.
4121  *
4122  * Originally Released Under LGPL - original licence link has changed is not relivant.
4123  *
4124  * Fork - LGPL
4125  * <script type="text/javascript">
4126  */
4127 /**
4128  * @class Roo.dd.DropZone
4129  * @extends Roo.dd.DropTarget
4130  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4131  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4132  * @constructor
4133  * @param {String/HTMLElement/Element} el The container element
4134  * @param {Object} config
4135  */
4136 Roo.dd.DropZone = function(el, config){
4137     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4138 };
4139
4140 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4141     /**
4142      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4143      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4144      * provide your own custom lookup.
4145      * @param {Event} e The event
4146      * @return {Object} data The custom data
4147      */
4148     getTargetFromEvent : function(e){
4149         return Roo.dd.Registry.getTargetFromEvent(e);
4150     },
4151
4152     /**
4153      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4154      * that it has registered.  This method has no default implementation and should be overridden to provide
4155      * node-specific processing if necessary.
4156      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4157      * {@link #getTargetFromEvent} for this node)
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      */
4162     onNodeEnter : function(n, dd, e, data){
4163         
4164     },
4165
4166     /**
4167      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4168      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4169      * overridden to provide the proper feedback.
4170      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4171      * {@link #getTargetFromEvent} for this node)
4172      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4173      * @param {Event} e The event
4174      * @param {Object} data An object containing arbitrary data supplied by the drag source
4175      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4176      * underlying {@link Roo.dd.StatusProxy} can be updated
4177      */
4178     onNodeOver : function(n, dd, e, data){
4179         return this.dropAllowed;
4180     },
4181
4182     /**
4183      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4184      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4185      * node-specific processing if necessary.
4186      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4187      * {@link #getTargetFromEvent} for this node)
4188      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4189      * @param {Event} e The event
4190      * @param {Object} data An object containing arbitrary data supplied by the drag source
4191      */
4192     onNodeOut : function(n, dd, e, data){
4193         
4194     },
4195
4196     /**
4197      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4198      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4199      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4200      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4201      * {@link #getTargetFromEvent} for this node)
4202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4203      * @param {Event} e The event
4204      * @param {Object} data An object containing arbitrary data supplied by the drag source
4205      * @return {Boolean} True if the drop was valid, else false
4206      */
4207     onNodeDrop : function(n, dd, e, data){
4208         return false;
4209     },
4210
4211     /**
4212      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4213      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4214      * it should be overridden to provide the proper feedback if necessary.
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4219      * underlying {@link Roo.dd.StatusProxy} can be updated
4220      */
4221     onContainerOver : function(dd, e, data){
4222         return this.dropNotAllowed;
4223     },
4224
4225     /**
4226      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4227      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4228      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4229      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4230      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4231      * @param {Event} e The event
4232      * @param {Object} data An object containing arbitrary data supplied by the drag source
4233      * @return {Boolean} True if the drop was valid, else false
4234      */
4235     onContainerDrop : function(dd, e, data){
4236         return false;
4237     },
4238
4239     /**
4240      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4241      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4242      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4243      * you should override this method and provide a custom implementation.
4244      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4245      * @param {Event} e The event
4246      * @param {Object} data An object containing arbitrary data supplied by the drag source
4247      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4248      * underlying {@link Roo.dd.StatusProxy} can be updated
4249      */
4250     notifyEnter : function(dd, e, data){
4251         return this.dropNotAllowed;
4252     },
4253
4254     /**
4255      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4256      * This method will be called on every mouse movement while the drag source is over the drop zone.
4257      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4258      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4259      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4260      * registered node, it will call {@link #onContainerOver}.
4261      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4262      * @param {Event} e The event
4263      * @param {Object} data An object containing arbitrary data supplied by the drag source
4264      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4265      * underlying {@link Roo.dd.StatusProxy} can be updated
4266      */
4267     notifyOver : function(dd, e, data){
4268         var n = this.getTargetFromEvent(e);
4269         if(!n){ // not over valid drop target
4270             if(this.lastOverNode){
4271                 this.onNodeOut(this.lastOverNode, dd, e, data);
4272                 this.lastOverNode = null;
4273             }
4274             return this.onContainerOver(dd, e, data);
4275         }
4276         if(this.lastOverNode != n){
4277             if(this.lastOverNode){
4278                 this.onNodeOut(this.lastOverNode, dd, e, data);
4279             }
4280             this.onNodeEnter(n, dd, e, data);
4281             this.lastOverNode = n;
4282         }
4283         return this.onNodeOver(n, dd, e, data);
4284     },
4285
4286     /**
4287      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4288      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4289      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4290      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4291      * @param {Event} e The event
4292      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4293      */
4294     notifyOut : function(dd, e, data){
4295         if(this.lastOverNode){
4296             this.onNodeOut(this.lastOverNode, dd, e, data);
4297             this.lastOverNode = null;
4298         }
4299     },
4300
4301     /**
4302      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4303      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4304      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4305      * otherwise it will call {@link #onContainerDrop}.
4306      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4307      * @param {Event} e The event
4308      * @param {Object} data An object containing arbitrary data supplied by the drag source
4309      * @return {Boolean} True if the drop was valid, else false
4310      */
4311     notifyDrop : function(dd, e, data){
4312         if(this.lastOverNode){
4313             this.onNodeOut(this.lastOverNode, dd, e, data);
4314             this.lastOverNode = null;
4315         }
4316         var n = this.getTargetFromEvent(e);
4317         return n ?
4318             this.onNodeDrop(n, dd, e, data) :
4319             this.onContainerDrop(dd, e, data);
4320     },
4321
4322     // private
4323     triggerCacheRefresh : function(){
4324         Roo.dd.DDM.refreshCache(this.groups);
4325     }  
4326 });/*
4327  * Based on:
4328  * Ext JS Library 1.1.1
4329  * Copyright(c) 2006-2007, Ext JS, LLC.
4330  *
4331  * Originally Released Under LGPL - original licence link has changed is not relivant.
4332  *
4333  * Fork - LGPL
4334  * <script type="text/javascript">
4335  */
4336
4337
4338 /**
4339  * @class Roo.data.SortTypes
4340  * @singleton
4341  * Defines the default sorting (casting?) comparison functions used when sorting data.
4342  */
4343 Roo.data.SortTypes = {
4344     /**
4345      * Default sort that does nothing
4346      * @param {Mixed} s The value being converted
4347      * @return {Mixed} The comparison value
4348      */
4349     none : function(s){
4350         return s;
4351     },
4352     
4353     /**
4354      * The regular expression used to strip tags
4355      * @type {RegExp}
4356      * @property
4357      */
4358     stripTagsRE : /<\/?[^>]+>/gi,
4359     
4360     /**
4361      * Strips all HTML tags to sort on text only
4362      * @param {Mixed} s The value being converted
4363      * @return {String} The comparison value
4364      */
4365     asText : function(s){
4366         return String(s).replace(this.stripTagsRE, "");
4367     },
4368     
4369     /**
4370      * Strips all HTML tags to sort on text only - Case insensitive
4371      * @param {Mixed} s The value being converted
4372      * @return {String} The comparison value
4373      */
4374     asUCText : function(s){
4375         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4376     },
4377     
4378     /**
4379      * Case insensitive string
4380      * @param {Mixed} s The value being converted
4381      * @return {String} The comparison value
4382      */
4383     asUCString : function(s) {
4384         return String(s).toUpperCase();
4385     },
4386     
4387     /**
4388      * Date sorting
4389      * @param {Mixed} s The value being converted
4390      * @return {Number} The comparison value
4391      */
4392     asDate : function(s) {
4393         if(!s){
4394             return 0;
4395         }
4396         if(s instanceof Date){
4397             return s.getTime();
4398         }
4399         return Date.parse(String(s));
4400     },
4401     
4402     /**
4403      * Float sorting
4404      * @param {Mixed} s The value being converted
4405      * @return {Float} The comparison value
4406      */
4407     asFloat : function(s) {
4408         var val = parseFloat(String(s).replace(/,/g, ""));
4409         if(isNaN(val)) val = 0;
4410         return val;
4411     },
4412     
4413     /**
4414      * Integer sorting
4415      * @param {Mixed} s The value being converted
4416      * @return {Number} The comparison value
4417      */
4418     asInt : function(s) {
4419         var val = parseInt(String(s).replace(/,/g, ""));
4420         if(isNaN(val)) val = 0;
4421         return val;
4422     }
4423 };/*
4424  * Based on:
4425  * Ext JS Library 1.1.1
4426  * Copyright(c) 2006-2007, Ext JS, LLC.
4427  *
4428  * Originally Released Under LGPL - original licence link has changed is not relivant.
4429  *
4430  * Fork - LGPL
4431  * <script type="text/javascript">
4432  */
4433
4434 /**
4435 * @class Roo.data.Record
4436  * Instances of this class encapsulate both record <em>definition</em> information, and record
4437  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4438  * to access Records cached in an {@link Roo.data.Store} object.<br>
4439  * <p>
4440  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4441  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4442  * objects.<br>
4443  * <p>
4444  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4445  * @constructor
4446  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4447  * {@link #create}. The parameters are the same.
4448  * @param {Array} data An associative Array of data values keyed by the field name.
4449  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4450  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4451  * not specified an integer id is generated.
4452  */
4453 Roo.data.Record = function(data, id){
4454     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4455     this.data = data;
4456 };
4457
4458 /**
4459  * Generate a constructor for a specific record layout.
4460  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4461  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4462  * Each field definition object may contain the following properties: <ul>
4463  * <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,
4464  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4465  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4466  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4467  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4468  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4469  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4470  * this may be omitted.</p></li>
4471  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4472  * <ul><li>auto (Default, implies no conversion)</li>
4473  * <li>string</li>
4474  * <li>int</li>
4475  * <li>float</li>
4476  * <li>boolean</li>
4477  * <li>date</li></ul></p></li>
4478  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4479  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4480  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4481  * by the Reader into an object that will be stored in the Record. It is passed the
4482  * following parameters:<ul>
4483  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4484  * </ul></p></li>
4485  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4486  * </ul>
4487  * <br>usage:<br><pre><code>
4488 var TopicRecord = Roo.data.Record.create(
4489     {name: 'title', mapping: 'topic_title'},
4490     {name: 'author', mapping: 'username'},
4491     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4492     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4493     {name: 'lastPoster', mapping: 'user2'},
4494     {name: 'excerpt', mapping: 'post_text'}
4495 );
4496
4497 var myNewRecord = new TopicRecord({
4498     title: 'Do my job please',
4499     author: 'noobie',
4500     totalPosts: 1,
4501     lastPost: new Date(),
4502     lastPoster: 'Animal',
4503     excerpt: 'No way dude!'
4504 });
4505 myStore.add(myNewRecord);
4506 </code></pre>
4507  * @method create
4508  * @static
4509  */
4510 Roo.data.Record.create = function(o){
4511     var f = function(){
4512         f.superclass.constructor.apply(this, arguments);
4513     };
4514     Roo.extend(f, Roo.data.Record);
4515     var p = f.prototype;
4516     p.fields = new Roo.util.MixedCollection(false, function(field){
4517         return field.name;
4518     });
4519     for(var i = 0, len = o.length; i < len; i++){
4520         p.fields.add(new Roo.data.Field(o[i]));
4521     }
4522     f.getField = function(name){
4523         return p.fields.get(name);  
4524     };
4525     return f;
4526 };
4527
4528 Roo.data.Record.AUTO_ID = 1000;
4529 Roo.data.Record.EDIT = 'edit';
4530 Roo.data.Record.REJECT = 'reject';
4531 Roo.data.Record.COMMIT = 'commit';
4532
4533 Roo.data.Record.prototype = {
4534     /**
4535      * Readonly flag - true if this record has been modified.
4536      * @type Boolean
4537      */
4538     dirty : false,
4539     editing : false,
4540     error: null,
4541     modified: null,
4542
4543     // private
4544     join : function(store){
4545         this.store = store;
4546     },
4547
4548     /**
4549      * Set the named field to the specified value.
4550      * @param {String} name The name of the field to set.
4551      * @param {Object} value The value to set the field to.
4552      */
4553     set : function(name, value){
4554         if(this.data[name] == value){
4555             return;
4556         }
4557         this.dirty = true;
4558         if(!this.modified){
4559             this.modified = {};
4560         }
4561         if(typeof this.modified[name] == 'undefined'){
4562             this.modified[name] = this.data[name];
4563         }
4564         this.data[name] = value;
4565         if(!this.editing){
4566             this.store.afterEdit(this);
4567         }       
4568     },
4569
4570     /**
4571      * Get the value of the named field.
4572      * @param {String} name The name of the field to get the value of.
4573      * @return {Object} The value of the field.
4574      */
4575     get : function(name){
4576         return this.data[name]; 
4577     },
4578
4579     // private
4580     beginEdit : function(){
4581         this.editing = true;
4582         this.modified = {}; 
4583     },
4584
4585     // private
4586     cancelEdit : function(){
4587         this.editing = false;
4588         delete this.modified;
4589     },
4590
4591     // private
4592     endEdit : function(){
4593         this.editing = false;
4594         if(this.dirty && this.store){
4595             this.store.afterEdit(this);
4596         }
4597     },
4598
4599     /**
4600      * Usually called by the {@link Roo.data.Store} which owns the Record.
4601      * Rejects all changes made to the Record since either creation, or the last commit operation.
4602      * Modified fields are reverted to their original values.
4603      * <p>
4604      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4605      * of reject operations.
4606      */
4607     reject : function(){
4608         var m = this.modified;
4609         for(var n in m){
4610             if(typeof m[n] != "function"){
4611                 this.data[n] = m[n];
4612             }
4613         }
4614         this.dirty = false;
4615         delete this.modified;
4616         this.editing = false;
4617         if(this.store){
4618             this.store.afterReject(this);
4619         }
4620     },
4621
4622     /**
4623      * Usually called by the {@link Roo.data.Store} which owns the Record.
4624      * Commits all changes made to the Record since either creation, or the last commit operation.
4625      * <p>
4626      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4627      * of commit operations.
4628      */
4629     commit : function(){
4630         this.dirty = false;
4631         delete this.modified;
4632         this.editing = false;
4633         if(this.store){
4634             this.store.afterCommit(this);
4635         }
4636     },
4637
4638     // private
4639     hasError : function(){
4640         return this.error != null;
4641     },
4642
4643     // private
4644     clearError : function(){
4645         this.error = null;
4646     },
4647
4648     /**
4649      * Creates a copy of this record.
4650      * @param {String} id (optional) A new record id if you don't want to use this record's id
4651      * @return {Record}
4652      */
4653     copy : function(newId) {
4654         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4655     }
4656 };/*
4657  * Based on:
4658  * Ext JS Library 1.1.1
4659  * Copyright(c) 2006-2007, Ext JS, LLC.
4660  *
4661  * Originally Released Under LGPL - original licence link has changed is not relivant.
4662  *
4663  * Fork - LGPL
4664  * <script type="text/javascript">
4665  */
4666
4667
4668
4669 /**
4670  * @class Roo.data.Store
4671  * @extends Roo.util.Observable
4672  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4673  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4674  * <p>
4675  * 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
4676  * has no knowledge of the format of the data returned by the Proxy.<br>
4677  * <p>
4678  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4679  * instances from the data object. These records are cached and made available through accessor functions.
4680  * @constructor
4681  * Creates a new Store.
4682  * @param {Object} config A config object containing the objects needed for the Store to access data,
4683  * and read the data into Records.
4684  */
4685 Roo.data.Store = function(config){
4686     this.data = new Roo.util.MixedCollection(false);
4687     this.data.getKey = function(o){
4688         return o.id;
4689     };
4690     this.baseParams = {};
4691     // private
4692     this.paramNames = {
4693         "start" : "start",
4694         "limit" : "limit",
4695         "sort" : "sort",
4696         "dir" : "dir"
4697     };
4698
4699     if(config && config.data){
4700         this.inlineData = config.data;
4701         delete config.data;
4702     }
4703
4704     Roo.apply(this, config);
4705     
4706     if(this.reader){ // reader passed
4707         this.reader = Roo.factory(this.reader, Roo.data);
4708         this.reader.xmodule = this.xmodule || false;
4709         if(!this.recordType){
4710             this.recordType = this.reader.recordType;
4711         }
4712         if(this.reader.onMetaChange){
4713             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4714         }
4715     }
4716
4717     if(this.recordType){
4718         this.fields = this.recordType.prototype.fields;
4719     }
4720     this.modified = [];
4721
4722     this.addEvents({
4723         /**
4724          * @event datachanged
4725          * Fires when the data cache has changed, and a widget which is using this Store
4726          * as a Record cache should refresh its view.
4727          * @param {Store} this
4728          */
4729         datachanged : true,
4730         /**
4731          * @event metachange
4732          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4733          * @param {Store} this
4734          * @param {Object} meta The JSON metadata
4735          */
4736         metachange : true,
4737         /**
4738          * @event add
4739          * Fires when Records have been added to the Store
4740          * @param {Store} this
4741          * @param {Roo.data.Record[]} records The array of Records added
4742          * @param {Number} index The index at which the record(s) were added
4743          */
4744         add : true,
4745         /**
4746          * @event remove
4747          * Fires when a Record has been removed from the Store
4748          * @param {Store} this
4749          * @param {Roo.data.Record} record The Record that was removed
4750          * @param {Number} index The index at which the record was removed
4751          */
4752         remove : true,
4753         /**
4754          * @event update
4755          * Fires when a Record has been updated
4756          * @param {Store} this
4757          * @param {Roo.data.Record} record The Record that was updated
4758          * @param {String} operation The update operation being performed.  Value may be one of:
4759          * <pre><code>
4760  Roo.data.Record.EDIT
4761  Roo.data.Record.REJECT
4762  Roo.data.Record.COMMIT
4763          * </code></pre>
4764          */
4765         update : true,
4766         /**
4767          * @event clear
4768          * Fires when the data cache has been cleared.
4769          * @param {Store} this
4770          */
4771         clear : true,
4772         /**
4773          * @event beforeload
4774          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4775          * the load action will be canceled.
4776          * @param {Store} this
4777          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4778          */
4779         beforeload : true,
4780         /**
4781          * @event load
4782          * Fires after a new set of Records has been loaded.
4783          * @param {Store} this
4784          * @param {Roo.data.Record[]} records The Records that were loaded
4785          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4786          */
4787         load : true,
4788         /**
4789          * @event loadexception
4790          * Fires if an exception occurs in the Proxy during loading.
4791          * Called with the signature of the Proxy's "loadexception" event.
4792          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4793          * 
4794          * @param {Proxy} 
4795          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4796          * @param {Object} load options 
4797          * @param {Object} jsonData from your request (normally this contains the Exception)
4798          */
4799         loadexception : true
4800     });
4801     
4802     if(this.proxy){
4803         this.proxy = Roo.factory(this.proxy, Roo.data);
4804         this.proxy.xmodule = this.xmodule || false;
4805         this.relayEvents(this.proxy,  ["loadexception"]);
4806     }
4807     this.sortToggle = {};
4808
4809     Roo.data.Store.superclass.constructor.call(this);
4810
4811     if(this.inlineData){
4812         this.loadData(this.inlineData);
4813         delete this.inlineData;
4814     }
4815 };
4816 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4817      /**
4818     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4819     * without a remote query - used by combo/forms at present.
4820     */
4821     
4822     /**
4823     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4824     */
4825     /**
4826     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4827     */
4828     /**
4829     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4830     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4831     */
4832     /**
4833     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4834     * on any HTTP request
4835     */
4836     /**
4837     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4838     */
4839     /**
4840     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4841     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4842     */
4843     remoteSort : false,
4844
4845     /**
4846     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4847      * loaded or when a record is removed. (defaults to false).
4848     */
4849     pruneModifiedRecords : false,
4850
4851     // private
4852     lastOptions : null,
4853
4854     /**
4855      * Add Records to the Store and fires the add event.
4856      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4857      */
4858     add : function(records){
4859         records = [].concat(records);
4860         for(var i = 0, len = records.length; i < len; i++){
4861             records[i].join(this);
4862         }
4863         var index = this.data.length;
4864         this.data.addAll(records);
4865         this.fireEvent("add", this, records, index);
4866     },
4867
4868     /**
4869      * Remove a Record from the Store and fires the remove event.
4870      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4871      */
4872     remove : function(record){
4873         var index = this.data.indexOf(record);
4874         this.data.removeAt(index);
4875         if(this.pruneModifiedRecords){
4876             this.modified.remove(record);
4877         }
4878         this.fireEvent("remove", this, record, index);
4879     },
4880
4881     /**
4882      * Remove all Records from the Store and fires the clear event.
4883      */
4884     removeAll : function(){
4885         this.data.clear();
4886         if(this.pruneModifiedRecords){
4887             this.modified = [];
4888         }
4889         this.fireEvent("clear", this);
4890     },
4891
4892     /**
4893      * Inserts Records to the Store at the given index and fires the add event.
4894      * @param {Number} index The start index at which to insert the passed Records.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     insert : function(index, records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             this.data.insert(index, records[i]);
4901             records[i].join(this);
4902         }
4903         this.fireEvent("add", this, records, index);
4904     },
4905
4906     /**
4907      * Get the index within the cache of the passed Record.
4908      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4909      * @return {Number} The index of the passed Record. Returns -1 if not found.
4910      */
4911     indexOf : function(record){
4912         return this.data.indexOf(record);
4913     },
4914
4915     /**
4916      * Get the index within the cache of the Record with the passed id.
4917      * @param {String} id The id of the Record to find.
4918      * @return {Number} The index of the Record. Returns -1 if not found.
4919      */
4920     indexOfId : function(id){
4921         return this.data.indexOfKey(id);
4922     },
4923
4924     /**
4925      * Get the Record with the specified id.
4926      * @param {String} id The id of the Record to find.
4927      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4928      */
4929     getById : function(id){
4930         return this.data.key(id);
4931     },
4932
4933     /**
4934      * Get the Record at the specified index.
4935      * @param {Number} index The index of the Record to find.
4936      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4937      */
4938     getAt : function(index){
4939         return this.data.itemAt(index);
4940     },
4941
4942     /**
4943      * Returns a range of Records between specified indices.
4944      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4945      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4946      * @return {Roo.data.Record[]} An array of Records
4947      */
4948     getRange : function(start, end){
4949         return this.data.getRange(start, end);
4950     },
4951
4952     // private
4953     storeOptions : function(o){
4954         o = Roo.apply({}, o);
4955         delete o.callback;
4956         delete o.scope;
4957         this.lastOptions = o;
4958     },
4959
4960     /**
4961      * Loads the Record cache from the configured Proxy using the configured Reader.
4962      * <p>
4963      * If using remote paging, then the first load call must specify the <em>start</em>
4964      * and <em>limit</em> properties in the options.params property to establish the initial
4965      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4966      * <p>
4967      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4968      * and this call will return before the new data has been loaded. Perform any post-processing
4969      * in a callback function, or in a "load" event handler.</strong>
4970      * <p>
4971      * @param {Object} options An object containing properties which control loading options:<ul>
4972      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4973      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4974      * passed the following arguments:<ul>
4975      * <li>r : Roo.data.Record[]</li>
4976      * <li>options: Options object from the load call</li>
4977      * <li>success: Boolean success indicator</li></ul></li>
4978      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4979      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4980      * </ul>
4981      */
4982     load : function(options){
4983         options = options || {};
4984         if(this.fireEvent("beforeload", this, options) !== false){
4985             this.storeOptions(options);
4986             var p = Roo.apply(options.params || {}, this.baseParams);
4987             // if meta was not loaded from remote source.. try requesting it.
4988             if (!this.reader.metaFromRemote) {
4989                 p._requestMeta = 1;
4990             }
4991             if(this.sortInfo && this.remoteSort){
4992                 var pn = this.paramNames;
4993                 p[pn["sort"]] = this.sortInfo.field;
4994                 p[pn["dir"]] = this.sortInfo.direction;
4995             }
4996             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4997         }
4998     },
4999
5000     /**
5001      * Reloads the Record cache from the configured Proxy using the configured Reader and
5002      * the options from the last load operation performed.
5003      * @param {Object} options (optional) An object containing properties which may override the options
5004      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5005      * the most recently used options are reused).
5006      */
5007     reload : function(options){
5008         this.load(Roo.applyIf(options||{}, this.lastOptions));
5009     },
5010
5011     // private
5012     // Called as a callback by the Reader during a load operation.
5013     loadRecords : function(o, options, success){
5014         if(!o || success === false){
5015             if(success !== false){
5016                 this.fireEvent("load", this, [], options);
5017             }
5018             if(options.callback){
5019                 options.callback.call(options.scope || this, [], options, false);
5020             }
5021             return;
5022         }
5023         // if data returned failure - throw an exception.
5024         if (o.success === false) {
5025             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5026             return;
5027         }
5028         var r = o.records, t = o.totalRecords || r.length;
5029         if(!options || options.add !== true){
5030             if(this.pruneModifiedRecords){
5031                 this.modified = [];
5032             }
5033             for(var i = 0, len = r.length; i < len; i++){
5034                 r[i].join(this);
5035             }
5036             if(this.snapshot){
5037                 this.data = this.snapshot;
5038                 delete this.snapshot;
5039             }
5040             this.data.clear();
5041             this.data.addAll(r);
5042             this.totalLength = t;
5043             this.applySort();
5044             this.fireEvent("datachanged", this);
5045         }else{
5046             this.totalLength = Math.max(t, this.data.length+r.length);
5047             this.add(r);
5048         }
5049         this.fireEvent("load", this, r, options);
5050         if(options.callback){
5051             options.callback.call(options.scope || this, r, options, true);
5052         }
5053     },
5054
5055     /**
5056      * Loads data from a passed data block. A Reader which understands the format of the data
5057      * must have been configured in the constructor.
5058      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5059      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5060      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5061      */
5062     loadData : function(o, append){
5063         var r = this.reader.readRecords(o);
5064         this.loadRecords(r, {add: append}, true);
5065     },
5066
5067     /**
5068      * Gets the number of cached records.
5069      * <p>
5070      * <em>If using paging, this may not be the total size of the dataset. If the data object
5071      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5072      * the data set size</em>
5073      */
5074     getCount : function(){
5075         return this.data.length || 0;
5076     },
5077
5078     /**
5079      * Gets the total number of records in the dataset as returned by the server.
5080      * <p>
5081      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5082      * the dataset size</em>
5083      */
5084     getTotalCount : function(){
5085         return this.totalLength || 0;
5086     },
5087
5088     /**
5089      * Returns the sort state of the Store as an object with two properties:
5090      * <pre><code>
5091  field {String} The name of the field by which the Records are sorted
5092  direction {String} The sort order, "ASC" or "DESC"
5093      * </code></pre>
5094      */
5095     getSortState : function(){
5096         return this.sortInfo;
5097     },
5098
5099     // private
5100     applySort : function(){
5101         if(this.sortInfo && !this.remoteSort){
5102             var s = this.sortInfo, f = s.field;
5103             var st = this.fields.get(f).sortType;
5104             var fn = function(r1, r2){
5105                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5106                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5107             };
5108             this.data.sort(s.direction, fn);
5109             if(this.snapshot && this.snapshot != this.data){
5110                 this.snapshot.sort(s.direction, fn);
5111             }
5112         }
5113     },
5114
5115     /**
5116      * Sets the default sort column and order to be used by the next load operation.
5117      * @param {String} fieldName The name of the field to sort by.
5118      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5119      */
5120     setDefaultSort : function(field, dir){
5121         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5122     },
5123
5124     /**
5125      * Sort the Records.
5126      * If remote sorting is used, the sort is performed on the server, and the cache is
5127      * reloaded. If local sorting is used, the cache is sorted internally.
5128      * @param {String} fieldName The name of the field to sort by.
5129      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5130      */
5131     sort : function(fieldName, dir){
5132         var f = this.fields.get(fieldName);
5133         if(!dir){
5134             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5135                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5136             }else{
5137                 dir = f.sortDir;
5138             }
5139         }
5140         this.sortToggle[f.name] = dir;
5141         this.sortInfo = {field: f.name, direction: dir};
5142         if(!this.remoteSort){
5143             this.applySort();
5144             this.fireEvent("datachanged", this);
5145         }else{
5146             this.load(this.lastOptions);
5147         }
5148     },
5149
5150     /**
5151      * Calls the specified function for each of the Records in the cache.
5152      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5153      * Returning <em>false</em> aborts and exits the iteration.
5154      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5155      */
5156     each : function(fn, scope){
5157         this.data.each(fn, scope);
5158     },
5159
5160     /**
5161      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5162      * (e.g., during paging).
5163      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5164      */
5165     getModifiedRecords : function(){
5166         return this.modified;
5167     },
5168
5169     // private
5170     createFilterFn : function(property, value, anyMatch){
5171         if(!value.exec){ // not a regex
5172             value = String(value);
5173             if(value.length == 0){
5174                 return false;
5175             }
5176             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5177         }
5178         return function(r){
5179             return value.test(r.data[property]);
5180         };
5181     },
5182
5183     /**
5184      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5185      * @param {String} property A field on your records
5186      * @param {Number} start The record index to start at (defaults to 0)
5187      * @param {Number} end The last record index to include (defaults to length - 1)
5188      * @return {Number} The sum
5189      */
5190     sum : function(property, start, end){
5191         var rs = this.data.items, v = 0;
5192         start = start || 0;
5193         end = (end || end === 0) ? end : rs.length-1;
5194
5195         for(var i = start; i <= end; i++){
5196             v += (rs[i].data[property] || 0);
5197         }
5198         return v;
5199     },
5200
5201     /**
5202      * Filter the records by a specified property.
5203      * @param {String} field A field on your records
5204      * @param {String/RegExp} value Either a string that the field
5205      * should start with or a RegExp to test against the field
5206      * @param {Boolean} anyMatch True to match any part not just the beginning
5207      */
5208     filter : function(property, value, anyMatch){
5209         var fn = this.createFilterFn(property, value, anyMatch);
5210         return fn ? this.filterBy(fn) : this.clearFilter();
5211     },
5212
5213     /**
5214      * Filter by a function. The specified function will be called with each
5215      * record in this data source. If the function returns true the record is included,
5216      * otherwise it is filtered.
5217      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5218      * @param {Object} scope (optional) The scope of the function (defaults to this)
5219      */
5220     filterBy : function(fn, scope){
5221         this.snapshot = this.snapshot || this.data;
5222         this.data = this.queryBy(fn, scope||this);
5223         this.fireEvent("datachanged", this);
5224     },
5225
5226     /**
5227      * Query the records by a specified property.
5228      * @param {String} field A field on your records
5229      * @param {String/RegExp} value Either a string that the field
5230      * should start with or a RegExp to test against the field
5231      * @param {Boolean} anyMatch True to match any part not just the beginning
5232      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5233      */
5234     query : function(property, value, anyMatch){
5235         var fn = this.createFilterFn(property, value, anyMatch);
5236         return fn ? this.queryBy(fn) : this.data.clone();
5237     },
5238
5239     /**
5240      * Query by a function. The specified function will be called with each
5241      * record in this data source. If the function returns true the record is included
5242      * in the results.
5243      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5244      * @param {Object} scope (optional) The scope of the function (defaults to this)
5245       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5246      **/
5247     queryBy : function(fn, scope){
5248         var data = this.snapshot || this.data;
5249         return data.filterBy(fn, scope||this);
5250     },
5251
5252     /**
5253      * Collects unique values for a particular dataIndex from this store.
5254      * @param {String} dataIndex The property to collect
5255      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5256      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5257      * @return {Array} An array of the unique values
5258      **/
5259     collect : function(dataIndex, allowNull, bypassFilter){
5260         var d = (bypassFilter === true && this.snapshot) ?
5261                 this.snapshot.items : this.data.items;
5262         var v, sv, r = [], l = {};
5263         for(var i = 0, len = d.length; i < len; i++){
5264             v = d[i].data[dataIndex];
5265             sv = String(v);
5266             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5267                 l[sv] = true;
5268                 r[r.length] = v;
5269             }
5270         }
5271         return r;
5272     },
5273
5274     /**
5275      * Revert to a view of the Record cache with no filtering applied.
5276      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5277      */
5278     clearFilter : function(suppressEvent){
5279         if(this.snapshot && this.snapshot != this.data){
5280             this.data = this.snapshot;
5281             delete this.snapshot;
5282             if(suppressEvent !== true){
5283                 this.fireEvent("datachanged", this);
5284             }
5285         }
5286     },
5287
5288     // private
5289     afterEdit : function(record){
5290         if(this.modified.indexOf(record) == -1){
5291             this.modified.push(record);
5292         }
5293         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5294     },
5295
5296     // private
5297     afterReject : function(record){
5298         this.modified.remove(record);
5299         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5300     },
5301
5302     // private
5303     afterCommit : function(record){
5304         this.modified.remove(record);
5305         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5306     },
5307
5308     /**
5309      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5310      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5311      */
5312     commitChanges : function(){
5313         var m = this.modified.slice(0);
5314         this.modified = [];
5315         for(var i = 0, len = m.length; i < len; i++){
5316             m[i].commit();
5317         }
5318     },
5319
5320     /**
5321      * Cancel outstanding changes on all changed records.
5322      */
5323     rejectChanges : function(){
5324         var m = this.modified.slice(0);
5325         this.modified = [];
5326         for(var i = 0, len = m.length; i < len; i++){
5327             m[i].reject();
5328         }
5329     },
5330
5331     onMetaChange : function(meta, rtype, o){
5332         this.recordType = rtype;
5333         this.fields = rtype.prototype.fields;
5334         delete this.snapshot;
5335         this.sortInfo = meta.sortInfo || this.sortInfo;
5336         this.modified = [];
5337         this.fireEvent('metachange', this, this.reader.meta);
5338     }
5339 });/*
5340  * Based on:
5341  * Ext JS Library 1.1.1
5342  * Copyright(c) 2006-2007, Ext JS, LLC.
5343  *
5344  * Originally Released Under LGPL - original licence link has changed is not relivant.
5345  *
5346  * Fork - LGPL
5347  * <script type="text/javascript">
5348  */
5349
5350 /**
5351  * @class Roo.data.SimpleStore
5352  * @extends Roo.data.Store
5353  * Small helper class to make creating Stores from Array data easier.
5354  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5355  * @cfg {Array} fields An array of field definition objects, or field name strings.
5356  * @cfg {Array} data The multi-dimensional array of data
5357  * @constructor
5358  * @param {Object} config
5359  */
5360 Roo.data.SimpleStore = function(config){
5361     Roo.data.SimpleStore.superclass.constructor.call(this, {
5362         isLocal : true,
5363         reader: new Roo.data.ArrayReader({
5364                 id: config.id
5365             },
5366             Roo.data.Record.create(config.fields)
5367         ),
5368         proxy : new Roo.data.MemoryProxy(config.data)
5369     });
5370     this.load();
5371 };
5372 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5373  * Based on:
5374  * Ext JS Library 1.1.1
5375  * Copyright(c) 2006-2007, Ext JS, LLC.
5376  *
5377  * Originally Released Under LGPL - original licence link has changed is not relivant.
5378  *
5379  * Fork - LGPL
5380  * <script type="text/javascript">
5381  */
5382
5383 /**
5384 /**
5385  * @extends Roo.data.Store
5386  * @class Roo.data.JsonStore
5387  * Small helper class to make creating Stores for JSON data easier. <br/>
5388 <pre><code>
5389 var store = new Roo.data.JsonStore({
5390     url: 'get-images.php',
5391     root: 'images',
5392     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5393 });
5394 </code></pre>
5395  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5396  * JsonReader and HttpProxy (unless inline data is provided).</b>
5397  * @cfg {Array} fields An array of field definition objects, or field name strings.
5398  * @constructor
5399  * @param {Object} config
5400  */
5401 Roo.data.JsonStore = function(c){
5402     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5403         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5404         reader: new Roo.data.JsonReader(c, c.fields)
5405     }));
5406 };
5407 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5408  * Based on:
5409  * Ext JS Library 1.1.1
5410  * Copyright(c) 2006-2007, Ext JS, LLC.
5411  *
5412  * Originally Released Under LGPL - original licence link has changed is not relivant.
5413  *
5414  * Fork - LGPL
5415  * <script type="text/javascript">
5416  */
5417
5418  
5419 Roo.data.Field = function(config){
5420     if(typeof config == "string"){
5421         config = {name: config};
5422     }
5423     Roo.apply(this, config);
5424     
5425     if(!this.type){
5426         this.type = "auto";
5427     }
5428     
5429     var st = Roo.data.SortTypes;
5430     // named sortTypes are supported, here we look them up
5431     if(typeof this.sortType == "string"){
5432         this.sortType = st[this.sortType];
5433     }
5434     
5435     // set default sortType for strings and dates
5436     if(!this.sortType){
5437         switch(this.type){
5438             case "string":
5439                 this.sortType = st.asUCString;
5440                 break;
5441             case "date":
5442                 this.sortType = st.asDate;
5443                 break;
5444             default:
5445                 this.sortType = st.none;
5446         }
5447     }
5448
5449     // define once
5450     var stripRe = /[\$,%]/g;
5451
5452     // prebuilt conversion function for this field, instead of
5453     // switching every time we're reading a value
5454     if(!this.convert){
5455         var cv, dateFormat = this.dateFormat;
5456         switch(this.type){
5457             case "":
5458             case "auto":
5459             case undefined:
5460                 cv = function(v){ return v; };
5461                 break;
5462             case "string":
5463                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5464                 break;
5465             case "int":
5466                 cv = function(v){
5467                     return v !== undefined && v !== null && v !== '' ?
5468                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5469                     };
5470                 break;
5471             case "float":
5472                 cv = function(v){
5473                     return v !== undefined && v !== null && v !== '' ?
5474                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5475                     };
5476                 break;
5477             case "bool":
5478             case "boolean":
5479                 cv = function(v){ return v === true || v === "true" || v == 1; };
5480                 break;
5481             case "date":
5482                 cv = function(v){
5483                     if(!v){
5484                         return '';
5485                     }
5486                     if(v instanceof Date){
5487                         return v;
5488                     }
5489                     if(dateFormat){
5490                         if(dateFormat == "timestamp"){
5491                             return new Date(v*1000);
5492                         }
5493                         return Date.parseDate(v, dateFormat);
5494                     }
5495                     var parsed = Date.parse(v);
5496                     return parsed ? new Date(parsed) : null;
5497                 };
5498              break;
5499             
5500         }
5501         this.convert = cv;
5502     }
5503 };
5504
5505 Roo.data.Field.prototype = {
5506     dateFormat: null,
5507     defaultValue: "",
5508     mapping: null,
5509     sortType : null,
5510     sortDir : "ASC"
5511 };/*
5512  * Based on:
5513  * Ext JS Library 1.1.1
5514  * Copyright(c) 2006-2007, Ext JS, LLC.
5515  *
5516  * Originally Released Under LGPL - original licence link has changed is not relivant.
5517  *
5518  * Fork - LGPL
5519  * <script type="text/javascript">
5520  */
5521  
5522 // Base class for reading structured data from a data source.  This class is intended to be
5523 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5524
5525 /**
5526  * @class Roo.data.DataReader
5527  * Base class for reading structured data from a data source.  This class is intended to be
5528  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5529  */
5530
5531 Roo.data.DataReader = function(meta, recordType){
5532     
5533     this.meta = meta;
5534     
5535     this.recordType = recordType instanceof Array ? 
5536         Roo.data.Record.create(recordType) : recordType;
5537 };
5538
5539 Roo.data.DataReader.prototype = {
5540      /**
5541      * Create an empty record
5542      * @param {Object} data (optional) - overlay some values
5543      * @return {Roo.data.Record} record created.
5544      */
5545     newRow :  function(d) {
5546         var da =  {};
5547         this.recordType.prototype.fields.each(function(c) {
5548             switch( c.type) {
5549                 case 'int' : da[c.name] = 0; break;
5550                 case 'date' : da[c.name] = new Date(); break;
5551                 case 'float' : da[c.name] = 0.0; break;
5552                 case 'boolean' : da[c.name] = false; break;
5553                 default : da[c.name] = ""; break;
5554             }
5555             
5556         });
5557         return new this.recordType(Roo.apply(da, d));
5558     }
5559     
5560 };/*
5561  * Based on:
5562  * Ext JS Library 1.1.1
5563  * Copyright(c) 2006-2007, Ext JS, LLC.
5564  *
5565  * Originally Released Under LGPL - original licence link has changed is not relivant.
5566  *
5567  * Fork - LGPL
5568  * <script type="text/javascript">
5569  */
5570
5571 /**
5572  * @class Roo.data.DataProxy
5573  * @extends Roo.data.Observable
5574  * This class is an abstract base class for implementations which provide retrieval of
5575  * unformatted data objects.<br>
5576  * <p>
5577  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5578  * (of the appropriate type which knows how to parse the data object) to provide a block of
5579  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5580  * <p>
5581  * Custom implementations must implement the load method as described in
5582  * {@link Roo.data.HttpProxy#load}.
5583  */
5584 Roo.data.DataProxy = function(){
5585     this.addEvents({
5586         /**
5587          * @event beforeload
5588          * Fires before a network request is made to retrieve a data object.
5589          * @param {Object} This DataProxy object.
5590          * @param {Object} params The params parameter to the load function.
5591          */
5592         beforeload : true,
5593         /**
5594          * @event load
5595          * Fires before the load method's callback is called.
5596          * @param {Object} This DataProxy object.
5597          * @param {Object} o The data object.
5598          * @param {Object} arg The callback argument object passed to the load function.
5599          */
5600         load : true,
5601         /**
5602          * @event loadexception
5603          * Fires if an Exception occurs during data retrieval.
5604          * @param {Object} This DataProxy object.
5605          * @param {Object} o The data object.
5606          * @param {Object} arg The callback argument object passed to the load function.
5607          * @param {Object} e The Exception.
5608          */
5609         loadexception : true
5610     });
5611     Roo.data.DataProxy.superclass.constructor.call(this);
5612 };
5613
5614 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5615
5616     /**
5617      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5618      */
5619 /*
5620  * Based on:
5621  * Ext JS Library 1.1.1
5622  * Copyright(c) 2006-2007, Ext JS, LLC.
5623  *
5624  * Originally Released Under LGPL - original licence link has changed is not relivant.
5625  *
5626  * Fork - LGPL
5627  * <script type="text/javascript">
5628  */
5629 /**
5630  * @class Roo.data.MemoryProxy
5631  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5632  * to the Reader when its load method is called.
5633  * @constructor
5634  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5635  */
5636 Roo.data.MemoryProxy = function(data){
5637     if (data.data) {
5638         data = data.data;
5639     }
5640     Roo.data.MemoryProxy.superclass.constructor.call(this);
5641     this.data = data;
5642 };
5643
5644 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5645     /**
5646      * Load data from the requested source (in this case an in-memory
5647      * data object passed to the constructor), read the data object into
5648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5649      * process that block using the passed callback.
5650      * @param {Object} params This parameter is not used by the MemoryProxy class.
5651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5652      * object into a block of Roo.data.Records.
5653      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5654      * The function must be passed <ul>
5655      * <li>The Record block object</li>
5656      * <li>The "arg" argument from the load function</li>
5657      * <li>A boolean success indicator</li>
5658      * </ul>
5659      * @param {Object} scope The scope in which to call the callback
5660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5661      */
5662     load : function(params, reader, callback, scope, arg){
5663         params = params || {};
5664         var result;
5665         try {
5666             result = reader.readRecords(this.data);
5667         }catch(e){
5668             this.fireEvent("loadexception", this, arg, null, e);
5669             callback.call(scope, null, arg, false);
5670             return;
5671         }
5672         callback.call(scope, result, arg, true);
5673     },
5674     
5675     // private
5676     update : function(params, records){
5677         
5678     }
5679 });/*
5680  * Based on:
5681  * Ext JS Library 1.1.1
5682  * Copyright(c) 2006-2007, Ext JS, LLC.
5683  *
5684  * Originally Released Under LGPL - original licence link has changed is not relivant.
5685  *
5686  * Fork - LGPL
5687  * <script type="text/javascript">
5688  */
5689 /**
5690  * @class Roo.data.HttpProxy
5691  * @extends Roo.data.DataProxy
5692  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5693  * configured to reference a certain URL.<br><br>
5694  * <p>
5695  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5696  * from which the running page was served.<br><br>
5697  * <p>
5698  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5699  * <p>
5700  * Be aware that to enable the browser to parse an XML document, the server must set
5701  * the Content-Type header in the HTTP response to "text/xml".
5702  * @constructor
5703  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5704  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5705  * will be used to make the request.
5706  */
5707 Roo.data.HttpProxy = function(conn){
5708     Roo.data.HttpProxy.superclass.constructor.call(this);
5709     // is conn a conn config or a real conn?
5710     this.conn = conn;
5711     this.useAjax = !conn || !conn.events;
5712   
5713 };
5714
5715 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5716     // thse are take from connection...
5717     
5718     /**
5719      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5720      */
5721     /**
5722      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5723      * extra parameters to each request made by this object. (defaults to undefined)
5724      */
5725     /**
5726      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5727      *  to each request made by this object. (defaults to undefined)
5728      */
5729     /**
5730      * @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)
5731      */
5732     /**
5733      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5734      */
5735      /**
5736      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5737      * @type Boolean
5738      */
5739   
5740
5741     /**
5742      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5743      * @type Boolean
5744      */
5745     /**
5746      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5747      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5748      * a finer-grained basis than the DataProxy events.
5749      */
5750     getConnection : function(){
5751         return this.useAjax ? Roo.Ajax : this.conn;
5752     },
5753
5754     /**
5755      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5756      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5757      * process that block using the passed callback.
5758      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5759      * for the request to the remote server.
5760      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5761      * object into a block of Roo.data.Records.
5762      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5763      * The function must be passed <ul>
5764      * <li>The Record block object</li>
5765      * <li>The "arg" argument from the load function</li>
5766      * <li>A boolean success indicator</li>
5767      * </ul>
5768      * @param {Object} scope The scope in which to call the callback
5769      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5770      */
5771     load : function(params, reader, callback, scope, arg){
5772         if(this.fireEvent("beforeload", this, params) !== false){
5773             var  o = {
5774                 params : params || {},
5775                 request: {
5776                     callback : callback,
5777                     scope : scope,
5778                     arg : arg
5779                 },
5780                 reader: reader,
5781                 callback : this.loadResponse,
5782                 scope: this
5783             };
5784             if(this.useAjax){
5785                 Roo.applyIf(o, this.conn);
5786                 if(this.activeRequest){
5787                     Roo.Ajax.abort(this.activeRequest);
5788                 }
5789                 this.activeRequest = Roo.Ajax.request(o);
5790             }else{
5791                 this.conn.request(o);
5792             }
5793         }else{
5794             callback.call(scope||this, null, arg, false);
5795         }
5796     },
5797
5798     // private
5799     loadResponse : function(o, success, response){
5800         delete this.activeRequest;
5801         if(!success){
5802             this.fireEvent("loadexception", this, o, response);
5803             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5804             return;
5805         }
5806         var result;
5807         try {
5808             result = o.reader.read(response);
5809         }catch(e){
5810             this.fireEvent("loadexception", this, o, response, e);
5811             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5812             return;
5813         }
5814         
5815         this.fireEvent("load", this, o, o.request.arg);
5816         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5817     },
5818
5819     // private
5820     update : function(dataSet){
5821
5822     },
5823
5824     // private
5825     updateResponse : function(dataSet){
5826
5827     }
5828 });/*
5829  * Based on:
5830  * Ext JS Library 1.1.1
5831  * Copyright(c) 2006-2007, Ext JS, LLC.
5832  *
5833  * Originally Released Under LGPL - original licence link has changed is not relivant.
5834  *
5835  * Fork - LGPL
5836  * <script type="text/javascript">
5837  */
5838
5839 /**
5840  * @class Roo.data.ScriptTagProxy
5841  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5842  * other than the originating domain of the running page.<br><br>
5843  * <p>
5844  * <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
5845  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5846  * <p>
5847  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5848  * source code that is used as the source inside a &lt;script> tag.<br><br>
5849  * <p>
5850  * In order for the browser to process the returned data, the server must wrap the data object
5851  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5852  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5853  * depending on whether the callback name was passed:
5854  * <p>
5855  * <pre><code>
5856 boolean scriptTag = false;
5857 String cb = request.getParameter("callback");
5858 if (cb != null) {
5859     scriptTag = true;
5860     response.setContentType("text/javascript");
5861 } else {
5862     response.setContentType("application/x-json");
5863 }
5864 Writer out = response.getWriter();
5865 if (scriptTag) {
5866     out.write(cb + "(");
5867 }
5868 out.print(dataBlock.toJsonString());
5869 if (scriptTag) {
5870     out.write(");");
5871 }
5872 </pre></code>
5873  *
5874  * @constructor
5875  * @param {Object} config A configuration object.
5876  */
5877 Roo.data.ScriptTagProxy = function(config){
5878     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5879     Roo.apply(this, config);
5880     this.head = document.getElementsByTagName("head")[0];
5881 };
5882
5883 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5884
5885 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5886     /**
5887      * @cfg {String} url The URL from which to request the data object.
5888      */
5889     /**
5890      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5891      */
5892     timeout : 30000,
5893     /**
5894      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5895      * the server the name of the callback function set up by the load call to process the returned data object.
5896      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5897      * javascript output which calls this named function passing the data object as its only parameter.
5898      */
5899     callbackParam : "callback",
5900     /**
5901      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5902      * name to the request.
5903      */
5904     nocache : true,
5905
5906     /**
5907      * Load data from the configured URL, read the data object into
5908      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5909      * process that block using the passed callback.
5910      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5911      * for the request to the remote server.
5912      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5913      * object into a block of Roo.data.Records.
5914      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5915      * The function must be passed <ul>
5916      * <li>The Record block object</li>
5917      * <li>The "arg" argument from the load function</li>
5918      * <li>A boolean success indicator</li>
5919      * </ul>
5920      * @param {Object} scope The scope in which to call the callback
5921      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5922      */
5923     load : function(params, reader, callback, scope, arg){
5924         if(this.fireEvent("beforeload", this, params) !== false){
5925
5926             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5927
5928             var url = this.url;
5929             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5930             if(this.nocache){
5931                 url += "&_dc=" + (new Date().getTime());
5932             }
5933             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5934             var trans = {
5935                 id : transId,
5936                 cb : "stcCallback"+transId,
5937                 scriptId : "stcScript"+transId,
5938                 params : params,
5939                 arg : arg,
5940                 url : url,
5941                 callback : callback,
5942                 scope : scope,
5943                 reader : reader
5944             };
5945             var conn = this;
5946
5947             window[trans.cb] = function(o){
5948                 conn.handleResponse(o, trans);
5949             };
5950
5951             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5952
5953             if(this.autoAbort !== false){
5954                 this.abort();
5955             }
5956
5957             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5958
5959             var script = document.createElement("script");
5960             script.setAttribute("src", url);
5961             script.setAttribute("type", "text/javascript");
5962             script.setAttribute("id", trans.scriptId);
5963             this.head.appendChild(script);
5964
5965             this.trans = trans;
5966         }else{
5967             callback.call(scope||this, null, arg, false);
5968         }
5969     },
5970
5971     // private
5972     isLoading : function(){
5973         return this.trans ? true : false;
5974     },
5975
5976     /**
5977      * Abort the current server request.
5978      */
5979     abort : function(){
5980         if(this.isLoading()){
5981             this.destroyTrans(this.trans);
5982         }
5983     },
5984
5985     // private
5986     destroyTrans : function(trans, isLoaded){
5987         this.head.removeChild(document.getElementById(trans.scriptId));
5988         clearTimeout(trans.timeoutId);
5989         if(isLoaded){
5990             window[trans.cb] = undefined;
5991             try{
5992                 delete window[trans.cb];
5993             }catch(e){}
5994         }else{
5995             // if hasn't been loaded, wait for load to remove it to prevent script error
5996             window[trans.cb] = function(){
5997                 window[trans.cb] = undefined;
5998                 try{
5999                     delete window[trans.cb];
6000                 }catch(e){}
6001             };
6002         }
6003     },
6004
6005     // private
6006     handleResponse : function(o, trans){
6007         this.trans = false;
6008         this.destroyTrans(trans, true);
6009         var result;
6010         try {
6011             result = trans.reader.readRecords(o);
6012         }catch(e){
6013             this.fireEvent("loadexception", this, o, trans.arg, e);
6014             trans.callback.call(trans.scope||window, null, trans.arg, false);
6015             return;
6016         }
6017         this.fireEvent("load", this, o, trans.arg);
6018         trans.callback.call(trans.scope||window, result, trans.arg, true);
6019     },
6020
6021     // private
6022     handleFailure : function(trans){
6023         this.trans = false;
6024         this.destroyTrans(trans, false);
6025         this.fireEvent("loadexception", this, null, trans.arg);
6026         trans.callback.call(trans.scope||window, null, trans.arg, false);
6027     }
6028 });/*
6029  * Based on:
6030  * Ext JS Library 1.1.1
6031  * Copyright(c) 2006-2007, Ext JS, LLC.
6032  *
6033  * Originally Released Under LGPL - original licence link has changed is not relivant.
6034  *
6035  * Fork - LGPL
6036  * <script type="text/javascript">
6037  */
6038
6039 /**
6040  * @class Roo.data.JsonReader
6041  * @extends Roo.data.DataReader
6042  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6043  * based on mappings in a provided Roo.data.Record constructor.
6044  * 
6045  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6046  * in the reply previously. 
6047  * 
6048  * <p>
6049  * Example code:
6050  * <pre><code>
6051 var RecordDef = Roo.data.Record.create([
6052     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6053     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6054 ]);
6055 var myReader = new Roo.data.JsonReader({
6056     totalProperty: "results",    // The property which contains the total dataset size (optional)
6057     root: "rows",                // The property which contains an Array of row objects
6058     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6059 }, RecordDef);
6060 </code></pre>
6061  * <p>
6062  * This would consume a JSON file like this:
6063  * <pre><code>
6064 { 'results': 2, 'rows': [
6065     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6066     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6067 }
6068 </code></pre>
6069  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6070  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6071  * paged from the remote server.
6072  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6073  * @cfg {String} root name of the property which contains the Array of row objects.
6074  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6075  * @constructor
6076  * Create a new JsonReader
6077  * @param {Object} meta Metadata configuration options
6078  * @param {Object} recordType Either an Array of field definition objects,
6079  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6080  */
6081 Roo.data.JsonReader = function(meta, recordType){
6082     
6083     meta = meta || {};
6084     // set some defaults:
6085     Roo.applyIf(meta, {
6086         totalProperty: 'total',
6087         successProperty : 'success',
6088         root : 'data',
6089         id : 'id'
6090     });
6091     
6092     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6093 };
6094 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6095     
6096     /**
6097      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6098      * Used by Store query builder to append _requestMeta to params.
6099      * 
6100      */
6101     metaFromRemote : false,
6102     /**
6103      * This method is only used by a DataProxy which has retrieved data from a remote server.
6104      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6105      * @return {Object} data A data block which is used by an Roo.data.Store object as
6106      * a cache of Roo.data.Records.
6107      */
6108     read : function(response){
6109         var json = response.responseText;
6110        
6111         var o = /* eval:var:o */ eval("("+json+")");
6112         if(!o) {
6113             throw {message: "JsonReader.read: Json object not found"};
6114         }
6115         
6116         if(o.metaData){
6117             
6118             delete this.ef;
6119             this.metaFromRemote = true;
6120             this.meta = o.metaData;
6121             this.recordType = Roo.data.Record.create(o.metaData.fields);
6122             this.onMetaChange(this.meta, this.recordType, o);
6123         }
6124         return this.readRecords(o);
6125     },
6126
6127     // private function a store will implement
6128     onMetaChange : function(meta, recordType, o){
6129
6130     },
6131
6132     /**
6133          * @ignore
6134          */
6135     simpleAccess: function(obj, subsc) {
6136         return obj[subsc];
6137     },
6138
6139         /**
6140          * @ignore
6141          */
6142     getJsonAccessor: function(){
6143         var re = /[\[\.]/;
6144         return function(expr) {
6145             try {
6146                 return(re.test(expr))
6147                     ? new Function("obj", "return obj." + expr)
6148                     : function(obj){
6149                         return obj[expr];
6150                     };
6151             } catch(e){}
6152             return Roo.emptyFn;
6153         };
6154     }(),
6155
6156     /**
6157      * Create a data block containing Roo.data.Records from an XML document.
6158      * @param {Object} o An object which contains an Array of row objects in the property specified
6159      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6160      * which contains the total size of the dataset.
6161      * @return {Object} data A data block which is used by an Roo.data.Store object as
6162      * a cache of Roo.data.Records.
6163      */
6164     readRecords : function(o){
6165         /**
6166          * After any data loads, the raw JSON data is available for further custom processing.
6167          * @type Object
6168          */
6169         this.jsonData = o;
6170         var s = this.meta, Record = this.recordType,
6171             f = Record.prototype.fields, fi = f.items, fl = f.length;
6172
6173 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6174         if (!this.ef) {
6175             if(s.totalProperty) {
6176                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6177                 }
6178                 if(s.successProperty) {
6179                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6180                 }
6181                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6182                 if (s.id) {
6183                         var g = this.getJsonAccessor(s.id);
6184                         this.getId = function(rec) {
6185                                 var r = g(rec);
6186                                 return (r === undefined || r === "") ? null : r;
6187                         };
6188                 } else {
6189                         this.getId = function(){return null;};
6190                 }
6191             this.ef = [];
6192             for(var jj = 0; jj < fl; jj++){
6193                 f = fi[jj];
6194                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6195                 this.ef[jj] = this.getJsonAccessor(map);
6196             }
6197         }
6198
6199         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6200         if(s.totalProperty){
6201             var vt = parseInt(this.getTotal(o), 10);
6202             if(!isNaN(vt)){
6203                 totalRecords = vt;
6204             }
6205         }
6206         if(s.successProperty){
6207             var vs = this.getSuccess(o);
6208             if(vs === false || vs === 'false'){
6209                 success = false;
6210             }
6211         }
6212         var records = [];
6213             for(var i = 0; i < c; i++){
6214                     var n = root[i];
6215                 var values = {};
6216                 var id = this.getId(n);
6217                 for(var j = 0; j < fl; j++){
6218                     f = fi[j];
6219                 var v = this.ef[j](n);
6220                 if (!f.convert) {
6221                     Roo.log('missing convert for ' + f.name);
6222                     Roo.log(f);
6223                     continue;
6224                 }
6225                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6226                 }
6227                 var record = new Record(values, id);
6228                 record.json = n;
6229                 records[i] = record;
6230             }
6231             return {
6232                 success : success,
6233                 records : records,
6234                 totalRecords : totalRecords
6235             };
6236     }
6237 });/*
6238  * Based on:
6239  * Ext JS Library 1.1.1
6240  * Copyright(c) 2006-2007, Ext JS, LLC.
6241  *
6242  * Originally Released Under LGPL - original licence link has changed is not relivant.
6243  *
6244  * Fork - LGPL
6245  * <script type="text/javascript">
6246  */
6247
6248 /**
6249  * @class Roo.data.XmlReader
6250  * @extends Roo.data.DataReader
6251  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6252  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6253  * <p>
6254  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6255  * header in the HTTP response must be set to "text/xml".</em>
6256  * <p>
6257  * Example code:
6258  * <pre><code>
6259 var RecordDef = Roo.data.Record.create([
6260    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6261    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6262 ]);
6263 var myReader = new Roo.data.XmlReader({
6264    totalRecords: "results", // The element which contains the total dataset size (optional)
6265    record: "row",           // The repeated element which contains row information
6266    id: "id"                 // The element within the row that provides an ID for the record (optional)
6267 }, RecordDef);
6268 </code></pre>
6269  * <p>
6270  * This would consume an XML file like this:
6271  * <pre><code>
6272 &lt;?xml?>
6273 &lt;dataset>
6274  &lt;results>2&lt;/results>
6275  &lt;row>
6276    &lt;id>1&lt;/id>
6277    &lt;name>Bill&lt;/name>
6278    &lt;occupation>Gardener&lt;/occupation>
6279  &lt;/row>
6280  &lt;row>
6281    &lt;id>2&lt;/id>
6282    &lt;name>Ben&lt;/name>
6283    &lt;occupation>Horticulturalist&lt;/occupation>
6284  &lt;/row>
6285 &lt;/dataset>
6286 </code></pre>
6287  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6288  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6289  * paged from the remote server.
6290  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6291  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6292  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6293  * a record identifier value.
6294  * @constructor
6295  * Create a new XmlReader
6296  * @param {Object} meta Metadata configuration options
6297  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6298  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6299  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6300  */
6301 Roo.data.XmlReader = function(meta, recordType){
6302     meta = meta || {};
6303     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6304 };
6305 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6306     /**
6307      * This method is only used by a DataProxy which has retrieved data from a remote server.
6308          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6309          * to contain a method called 'responseXML' that returns an XML document object.
6310      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6311      * a cache of Roo.data.Records.
6312      */
6313     read : function(response){
6314         var doc = response.responseXML;
6315         if(!doc) {
6316             throw {message: "XmlReader.read: XML Document not available"};
6317         }
6318         return this.readRecords(doc);
6319     },
6320
6321     /**
6322      * Create a data block containing Roo.data.Records from an XML document.
6323          * @param {Object} doc A parsed XML document.
6324      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6325      * a cache of Roo.data.Records.
6326      */
6327     readRecords : function(doc){
6328         /**
6329          * After any data loads/reads, the raw XML Document is available for further custom processing.
6330          * @type XMLDocument
6331          */
6332         this.xmlData = doc;
6333         var root = doc.documentElement || doc;
6334         var q = Roo.DomQuery;
6335         var recordType = this.recordType, fields = recordType.prototype.fields;
6336         var sid = this.meta.id;
6337         var totalRecords = 0, success = true;
6338         if(this.meta.totalRecords){
6339             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6340         }
6341         
6342         if(this.meta.success){
6343             var sv = q.selectValue(this.meta.success, root, true);
6344             success = sv !== false && sv !== 'false';
6345         }
6346         var records = [];
6347         var ns = q.select(this.meta.record, root);
6348         for(var i = 0, len = ns.length; i < len; i++) {
6349                 var n = ns[i];
6350                 var values = {};
6351                 var id = sid ? q.selectValue(sid, n) : undefined;
6352                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6353                     var f = fields.items[j];
6354                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6355                     v = f.convert(v);
6356                     values[f.name] = v;
6357                 }
6358                 var record = new recordType(values, id);
6359                 record.node = n;
6360                 records[records.length] = record;
6361             }
6362
6363             return {
6364                 success : success,
6365                 records : records,
6366                 totalRecords : totalRecords || records.length
6367             };
6368     }
6369 });/*
6370  * Based on:
6371  * Ext JS Library 1.1.1
6372  * Copyright(c) 2006-2007, Ext JS, LLC.
6373  *
6374  * Originally Released Under LGPL - original licence link has changed is not relivant.
6375  *
6376  * Fork - LGPL
6377  * <script type="text/javascript">
6378  */
6379
6380 /**
6381  * @class Roo.data.ArrayReader
6382  * @extends Roo.data.DataReader
6383  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6384  * Each element of that Array represents a row of data fields. The
6385  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6386  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6387  * <p>
6388  * Example code:.
6389  * <pre><code>
6390 var RecordDef = Roo.data.Record.create([
6391     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6392     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6393 ]);
6394 var myReader = new Roo.data.ArrayReader({
6395     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6396 }, RecordDef);
6397 </code></pre>
6398  * <p>
6399  * This would consume an Array like this:
6400  * <pre><code>
6401 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6402   </code></pre>
6403  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6404  * @constructor
6405  * Create a new JsonReader
6406  * @param {Object} meta Metadata configuration options.
6407  * @param {Object} recordType Either an Array of field definition objects
6408  * as specified to {@link Roo.data.Record#create},
6409  * or an {@link Roo.data.Record} object
6410  * created using {@link Roo.data.Record#create}.
6411  */
6412 Roo.data.ArrayReader = function(meta, recordType){
6413     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6414 };
6415
6416 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6417     /**
6418      * Create a data block containing Roo.data.Records from an XML document.
6419      * @param {Object} o An Array of row objects which represents the dataset.
6420      * @return {Object} data A data block which is used by an Roo.data.Store object as
6421      * a cache of Roo.data.Records.
6422      */
6423     readRecords : function(o){
6424         var sid = this.meta ? this.meta.id : null;
6425         var recordType = this.recordType, fields = recordType.prototype.fields;
6426         var records = [];
6427         var root = o;
6428             for(var i = 0; i < root.length; i++){
6429                     var n = root[i];
6430                 var values = {};
6431                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6432                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6433                 var f = fields.items[j];
6434                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6435                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6436                 v = f.convert(v);
6437                 values[f.name] = v;
6438             }
6439                 var record = new recordType(values, id);
6440                 record.json = n;
6441                 records[records.length] = record;
6442             }
6443             return {
6444                 records : records,
6445                 totalRecords : records.length
6446             };
6447     }
6448 });/*
6449  * Based on:
6450  * Ext JS Library 1.1.1
6451  * Copyright(c) 2006-2007, Ext JS, LLC.
6452  *
6453  * Originally Released Under LGPL - original licence link has changed is not relivant.
6454  *
6455  * Fork - LGPL
6456  * <script type="text/javascript">
6457  */
6458
6459
6460 /**
6461  * @class Roo.data.Tree
6462  * @extends Roo.util.Observable
6463  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6464  * in the tree have most standard DOM functionality.
6465  * @constructor
6466  * @param {Node} root (optional) The root node
6467  */
6468 Roo.data.Tree = function(root){
6469    this.nodeHash = {};
6470    /**
6471     * The root node for this tree
6472     * @type Node
6473     */
6474    this.root = null;
6475    if(root){
6476        this.setRootNode(root);
6477    }
6478    this.addEvents({
6479        /**
6480         * @event append
6481         * Fires when a new child node is appended to a node in this tree.
6482         * @param {Tree} tree The owner tree
6483         * @param {Node} parent The parent node
6484         * @param {Node} node The newly appended node
6485         * @param {Number} index The index of the newly appended node
6486         */
6487        "append" : true,
6488        /**
6489         * @event remove
6490         * Fires when a child node is removed from a node in this tree.
6491         * @param {Tree} tree The owner tree
6492         * @param {Node} parent The parent node
6493         * @param {Node} node The child node removed
6494         */
6495        "remove" : true,
6496        /**
6497         * @event move
6498         * Fires when a node is moved to a new location in the tree
6499         * @param {Tree} tree The owner tree
6500         * @param {Node} node The node moved
6501         * @param {Node} oldParent The old parent of this node
6502         * @param {Node} newParent The new parent of this node
6503         * @param {Number} index The index it was moved to
6504         */
6505        "move" : true,
6506        /**
6507         * @event insert
6508         * Fires when a new child node is inserted in a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The child node inserted
6512         * @param {Node} refNode The child node the node was inserted before
6513         */
6514        "insert" : true,
6515        /**
6516         * @event beforeappend
6517         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node to be appended
6521         */
6522        "beforeappend" : true,
6523        /**
6524         * @event beforeremove
6525         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} parent The parent node
6528         * @param {Node} node The child node to be removed
6529         */
6530        "beforeremove" : true,
6531        /**
6532         * @event beforemove
6533         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} node The node being moved
6536         * @param {Node} oldParent The parent of the node
6537         * @param {Node} newParent The new parent the node is moving to
6538         * @param {Number} index The index it is being moved to
6539         */
6540        "beforemove" : true,
6541        /**
6542         * @event beforeinsert
6543         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6544         * @param {Tree} tree The owner tree
6545         * @param {Node} parent The parent node
6546         * @param {Node} node The child node to be inserted
6547         * @param {Node} refNode The child node the node is being inserted before
6548         */
6549        "beforeinsert" : true
6550    });
6551
6552     Roo.data.Tree.superclass.constructor.call(this);
6553 };
6554
6555 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6556     pathSeparator: "/",
6557
6558     proxyNodeEvent : function(){
6559         return this.fireEvent.apply(this, arguments);
6560     },
6561
6562     /**
6563      * Returns the root node for this tree.
6564      * @return {Node}
6565      */
6566     getRootNode : function(){
6567         return this.root;
6568     },
6569
6570     /**
6571      * Sets the root node for this tree.
6572      * @param {Node} node
6573      * @return {Node}
6574      */
6575     setRootNode : function(node){
6576         this.root = node;
6577         node.ownerTree = this;
6578         node.isRoot = true;
6579         this.registerNode(node);
6580         return node;
6581     },
6582
6583     /**
6584      * Gets a node in this tree by its id.
6585      * @param {String} id
6586      * @return {Node}
6587      */
6588     getNodeById : function(id){
6589         return this.nodeHash[id];
6590     },
6591
6592     registerNode : function(node){
6593         this.nodeHash[node.id] = node;
6594     },
6595
6596     unregisterNode : function(node){
6597         delete this.nodeHash[node.id];
6598     },
6599
6600     toString : function(){
6601         return "[Tree"+(this.id?" "+this.id:"")+"]";
6602     }
6603 });
6604
6605 /**
6606  * @class Roo.data.Node
6607  * @extends Roo.util.Observable
6608  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6609  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6610  * @constructor
6611  * @param {Object} attributes The attributes/config for the node
6612  */
6613 Roo.data.Node = function(attributes){
6614     /**
6615      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6616      * @type {Object}
6617      */
6618     this.attributes = attributes || {};
6619     this.leaf = this.attributes.leaf;
6620     /**
6621      * The node id. @type String
6622      */
6623     this.id = this.attributes.id;
6624     if(!this.id){
6625         this.id = Roo.id(null, "ynode-");
6626         this.attributes.id = this.id;
6627     }
6628     /**
6629      * All child nodes of this node. @type Array
6630      */
6631     this.childNodes = [];
6632     if(!this.childNodes.indexOf){ // indexOf is a must
6633         this.childNodes.indexOf = function(o){
6634             for(var i = 0, len = this.length; i < len; i++){
6635                 if(this[i] == o) {
6636                     return i;
6637                 }
6638             }
6639             return -1;
6640         };
6641     }
6642     /**
6643      * The parent node for this node. @type Node
6644      */
6645     this.parentNode = null;
6646     /**
6647      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6648      */
6649     this.firstChild = null;
6650     /**
6651      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6652      */
6653     this.lastChild = null;
6654     /**
6655      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6656      */
6657     this.previousSibling = null;
6658     /**
6659      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6660      */
6661     this.nextSibling = null;
6662
6663     this.addEvents({
6664        /**
6665         * @event append
6666         * Fires when a new child node is appended
6667         * @param {Tree} tree The owner tree
6668         * @param {Node} this This node
6669         * @param {Node} node The newly appended node
6670         * @param {Number} index The index of the newly appended node
6671         */
6672        "append" : true,
6673        /**
6674         * @event remove
6675         * Fires when a child node is removed
6676         * @param {Tree} tree The owner tree
6677         * @param {Node} this This node
6678         * @param {Node} node The removed node
6679         */
6680        "remove" : true,
6681        /**
6682         * @event move
6683         * Fires when this node is moved to a new location in the tree
6684         * @param {Tree} tree The owner tree
6685         * @param {Node} this This node
6686         * @param {Node} oldParent The old parent of this node
6687         * @param {Node} newParent The new parent of this node
6688         * @param {Number} index The index it was moved to
6689         */
6690        "move" : true,
6691        /**
6692         * @event insert
6693         * Fires when a new child node is inserted.
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The child node inserted
6697         * @param {Node} refNode The child node the node was inserted before
6698         */
6699        "insert" : true,
6700        /**
6701         * @event beforeappend
6702         * Fires before a new child is appended, return false to cancel the append.
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The child node to be appended
6706         */
6707        "beforeappend" : true,
6708        /**
6709         * @event beforeremove
6710         * Fires before a child is removed, return false to cancel the remove.
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} node The child node to be removed
6714         */
6715        "beforeremove" : true,
6716        /**
6717         * @event beforemove
6718         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6719         * @param {Tree} tree The owner tree
6720         * @param {Node} this This node
6721         * @param {Node} oldParent The parent of this node
6722         * @param {Node} newParent The new parent this node is moving to
6723         * @param {Number} index The index it is being moved to
6724         */
6725        "beforemove" : true,
6726        /**
6727         * @event beforeinsert
6728         * Fires before a new child is inserted, return false to cancel the insert.
6729         * @param {Tree} tree The owner tree
6730         * @param {Node} this This node
6731         * @param {Node} node The child node to be inserted
6732         * @param {Node} refNode The child node the node is being inserted before
6733         */
6734        "beforeinsert" : true
6735    });
6736     this.listeners = this.attributes.listeners;
6737     Roo.data.Node.superclass.constructor.call(this);
6738 };
6739
6740 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6741     fireEvent : function(evtName){
6742         // first do standard event for this node
6743         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6744             return false;
6745         }
6746         // then bubble it up to the tree if the event wasn't cancelled
6747         var ot = this.getOwnerTree();
6748         if(ot){
6749             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6750                 return false;
6751             }
6752         }
6753         return true;
6754     },
6755
6756     /**
6757      * Returns true if this node is a leaf
6758      * @return {Boolean}
6759      */
6760     isLeaf : function(){
6761         return this.leaf === true;
6762     },
6763
6764     // private
6765     setFirstChild : function(node){
6766         this.firstChild = node;
6767     },
6768
6769     //private
6770     setLastChild : function(node){
6771         this.lastChild = node;
6772     },
6773
6774
6775     /**
6776      * Returns true if this node is the last child of its parent
6777      * @return {Boolean}
6778      */
6779     isLast : function(){
6780        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6781     },
6782
6783     /**
6784      * Returns true if this node is the first child of its parent
6785      * @return {Boolean}
6786      */
6787     isFirst : function(){
6788        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6789     },
6790
6791     hasChildNodes : function(){
6792         return !this.isLeaf() && this.childNodes.length > 0;
6793     },
6794
6795     /**
6796      * Insert node(s) as the last child node of this node.
6797      * @param {Node/Array} node The node or Array of nodes to append
6798      * @return {Node} The appended node if single append, or null if an array was passed
6799      */
6800     appendChild : function(node){
6801         var multi = false;
6802         if(node instanceof Array){
6803             multi = node;
6804         }else if(arguments.length > 1){
6805             multi = arguments;
6806         }
6807         // if passed an array or multiple args do them one by one
6808         if(multi){
6809             for(var i = 0, len = multi.length; i < len; i++) {
6810                 this.appendChild(multi[i]);
6811             }
6812         }else{
6813             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6814                 return false;
6815             }
6816             var index = this.childNodes.length;
6817             var oldParent = node.parentNode;
6818             // it's a move, make sure we move it cleanly
6819             if(oldParent){
6820                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6821                     return false;
6822                 }
6823                 oldParent.removeChild(node);
6824             }
6825             index = this.childNodes.length;
6826             if(index == 0){
6827                 this.setFirstChild(node);
6828             }
6829             this.childNodes.push(node);
6830             node.parentNode = this;
6831             var ps = this.childNodes[index-1];
6832             if(ps){
6833                 node.previousSibling = ps;
6834                 ps.nextSibling = node;
6835             }else{
6836                 node.previousSibling = null;
6837             }
6838             node.nextSibling = null;
6839             this.setLastChild(node);
6840             node.setOwnerTree(this.getOwnerTree());
6841             this.fireEvent("append", this.ownerTree, this, node, index);
6842             if(oldParent){
6843                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6844             }
6845             return node;
6846         }
6847     },
6848
6849     /**
6850      * Removes a child node from this node.
6851      * @param {Node} node The node to remove
6852      * @return {Node} The removed node
6853      */
6854     removeChild : function(node){
6855         var index = this.childNodes.indexOf(node);
6856         if(index == -1){
6857             return false;
6858         }
6859         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6860             return false;
6861         }
6862
6863         // remove it from childNodes collection
6864         this.childNodes.splice(index, 1);
6865
6866         // update siblings
6867         if(node.previousSibling){
6868             node.previousSibling.nextSibling = node.nextSibling;
6869         }
6870         if(node.nextSibling){
6871             node.nextSibling.previousSibling = node.previousSibling;
6872         }
6873
6874         // update child refs
6875         if(this.firstChild == node){
6876             this.setFirstChild(node.nextSibling);
6877         }
6878         if(this.lastChild == node){
6879             this.setLastChild(node.previousSibling);
6880         }
6881
6882         node.setOwnerTree(null);
6883         // clear any references from the node
6884         node.parentNode = null;
6885         node.previousSibling = null;
6886         node.nextSibling = null;
6887         this.fireEvent("remove", this.ownerTree, this, node);
6888         return node;
6889     },
6890
6891     /**
6892      * Inserts the first node before the second node in this nodes childNodes collection.
6893      * @param {Node} node The node to insert
6894      * @param {Node} refNode The node to insert before (if null the node is appended)
6895      * @return {Node} The inserted node
6896      */
6897     insertBefore : function(node, refNode){
6898         if(!refNode){ // like standard Dom, refNode can be null for append
6899             return this.appendChild(node);
6900         }
6901         // nothing to do
6902         if(node == refNode){
6903             return false;
6904         }
6905
6906         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6907             return false;
6908         }
6909         var index = this.childNodes.indexOf(refNode);
6910         var oldParent = node.parentNode;
6911         var refIndex = index;
6912
6913         // when moving internally, indexes will change after remove
6914         if(oldParent == this && this.childNodes.indexOf(node) < index){
6915             refIndex--;
6916         }
6917
6918         // it's a move, make sure we move it cleanly
6919         if(oldParent){
6920             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6921                 return false;
6922             }
6923             oldParent.removeChild(node);
6924         }
6925         if(refIndex == 0){
6926             this.setFirstChild(node);
6927         }
6928         this.childNodes.splice(refIndex, 0, node);
6929         node.parentNode = this;
6930         var ps = this.childNodes[refIndex-1];
6931         if(ps){
6932             node.previousSibling = ps;
6933             ps.nextSibling = node;
6934         }else{
6935             node.previousSibling = null;
6936         }
6937         node.nextSibling = refNode;
6938         refNode.previousSibling = node;
6939         node.setOwnerTree(this.getOwnerTree());
6940         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6941         if(oldParent){
6942             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6943         }
6944         return node;
6945     },
6946
6947     /**
6948      * Returns the child node at the specified index.
6949      * @param {Number} index
6950      * @return {Node}
6951      */
6952     item : function(index){
6953         return this.childNodes[index];
6954     },
6955
6956     /**
6957      * Replaces one child node in this node with another.
6958      * @param {Node} newChild The replacement node
6959      * @param {Node} oldChild The node to replace
6960      * @return {Node} The replaced node
6961      */
6962     replaceChild : function(newChild, oldChild){
6963         this.insertBefore(newChild, oldChild);
6964         this.removeChild(oldChild);
6965         return oldChild;
6966     },
6967
6968     /**
6969      * Returns the index of a child node
6970      * @param {Node} node
6971      * @return {Number} The index of the node or -1 if it was not found
6972      */
6973     indexOf : function(child){
6974         return this.childNodes.indexOf(child);
6975     },
6976
6977     /**
6978      * Returns the tree this node is in.
6979      * @return {Tree}
6980      */
6981     getOwnerTree : function(){
6982         // if it doesn't have one, look for one
6983         if(!this.ownerTree){
6984             var p = this;
6985             while(p){
6986                 if(p.ownerTree){
6987                     this.ownerTree = p.ownerTree;
6988                     break;
6989                 }
6990                 p = p.parentNode;
6991             }
6992         }
6993         return this.ownerTree;
6994     },
6995
6996     /**
6997      * Returns depth of this node (the root node has a depth of 0)
6998      * @return {Number}
6999      */
7000     getDepth : function(){
7001         var depth = 0;
7002         var p = this;
7003         while(p.parentNode){
7004             ++depth;
7005             p = p.parentNode;
7006         }
7007         return depth;
7008     },
7009
7010     // private
7011     setOwnerTree : function(tree){
7012         // if it's move, we need to update everyone
7013         if(tree != this.ownerTree){
7014             if(this.ownerTree){
7015                 this.ownerTree.unregisterNode(this);
7016             }
7017             this.ownerTree = tree;
7018             var cs = this.childNodes;
7019             for(var i = 0, len = cs.length; i < len; i++) {
7020                 cs[i].setOwnerTree(tree);
7021             }
7022             if(tree){
7023                 tree.registerNode(this);
7024             }
7025         }
7026     },
7027
7028     /**
7029      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7030      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7031      * @return {String} The path
7032      */
7033     getPath : function(attr){
7034         attr = attr || "id";
7035         var p = this.parentNode;
7036         var b = [this.attributes[attr]];
7037         while(p){
7038             b.unshift(p.attributes[attr]);
7039             p = p.parentNode;
7040         }
7041         var sep = this.getOwnerTree().pathSeparator;
7042         return sep + b.join(sep);
7043     },
7044
7045     /**
7046      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7047      * function call will be the scope provided or the current node. The arguments to the function
7048      * will be the args provided or the current node. If the function returns false at any point,
7049      * the bubble is stopped.
7050      * @param {Function} fn The function to call
7051      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7052      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7053      */
7054     bubble : function(fn, scope, args){
7055         var p = this;
7056         while(p){
7057             if(fn.call(scope || p, args || p) === false){
7058                 break;
7059             }
7060             p = p.parentNode;
7061         }
7062     },
7063
7064     /**
7065      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7066      * function call will be the scope provided or the current node. The arguments to the function
7067      * will be the args provided or the current node. If the function returns false at any point,
7068      * the cascade is stopped on that branch.
7069      * @param {Function} fn The function to call
7070      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7071      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7072      */
7073     cascade : function(fn, scope, args){
7074         if(fn.call(scope || this, args || this) !== false){
7075             var cs = this.childNodes;
7076             for(var i = 0, len = cs.length; i < len; i++) {
7077                 cs[i].cascade(fn, scope, args);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7084      * function call will be the scope provided or the current node. The arguments to the function
7085      * will be the args provided or the current node. If the function returns false at any point,
7086      * the iteration stops.
7087      * @param {Function} fn The function to call
7088      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7089      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7090      */
7091     eachChild : function(fn, scope, args){
7092         var cs = this.childNodes;
7093         for(var i = 0, len = cs.length; i < len; i++) {
7094                 if(fn.call(scope || this, args || cs[i]) === false){
7095                     break;
7096                 }
7097         }
7098     },
7099
7100     /**
7101      * Finds the first child that has the attribute with the specified value.
7102      * @param {String} attribute The attribute name
7103      * @param {Mixed} value The value to search for
7104      * @return {Node} The found child or null if none was found
7105      */
7106     findChild : function(attribute, value){
7107         var cs = this.childNodes;
7108         for(var i = 0, len = cs.length; i < len; i++) {
7109                 if(cs[i].attributes[attribute] == value){
7110                     return cs[i];
7111                 }
7112         }
7113         return null;
7114     },
7115
7116     /**
7117      * Finds the first child by a custom function. The child matches if the function passed
7118      * returns true.
7119      * @param {Function} fn
7120      * @param {Object} scope (optional)
7121      * @return {Node} The found child or null if none was found
7122      */
7123     findChildBy : function(fn, scope){
7124         var cs = this.childNodes;
7125         for(var i = 0, len = cs.length; i < len; i++) {
7126                 if(fn.call(scope||cs[i], cs[i]) === true){
7127                     return cs[i];
7128                 }
7129         }
7130         return null;
7131     },
7132
7133     /**
7134      * Sorts this nodes children using the supplied sort function
7135      * @param {Function} fn
7136      * @param {Object} scope (optional)
7137      */
7138     sort : function(fn, scope){
7139         var cs = this.childNodes;
7140         var len = cs.length;
7141         if(len > 0){
7142             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7143             cs.sort(sortFn);
7144             for(var i = 0; i < len; i++){
7145                 var n = cs[i];
7146                 n.previousSibling = cs[i-1];
7147                 n.nextSibling = cs[i+1];
7148                 if(i == 0){
7149                     this.setFirstChild(n);
7150                 }
7151                 if(i == len-1){
7152                     this.setLastChild(n);
7153                 }
7154             }
7155         }
7156     },
7157
7158     /**
7159      * Returns true if this node is an ancestor (at any point) of the passed node.
7160      * @param {Node} node
7161      * @return {Boolean}
7162      */
7163     contains : function(node){
7164         return node.isAncestor(this);
7165     },
7166
7167     /**
7168      * Returns true if the passed node is an ancestor (at any point) of this node.
7169      * @param {Node} node
7170      * @return {Boolean}
7171      */
7172     isAncestor : function(node){
7173         var p = this.parentNode;
7174         while(p){
7175             if(p == node){
7176                 return true;
7177             }
7178             p = p.parentNode;
7179         }
7180         return false;
7181     },
7182
7183     toString : function(){
7184         return "[Node"+(this.id?" "+this.id:"")+"]";
7185     }
7186 });/*
7187  * Based on:
7188  * Ext JS Library 1.1.1
7189  * Copyright(c) 2006-2007, Ext JS, LLC.
7190  *
7191  * Originally Released Under LGPL - original licence link has changed is not relivant.
7192  *
7193  * Fork - LGPL
7194  * <script type="text/javascript">
7195  */
7196  
7197
7198 /**
7199  * @class Roo.ComponentMgr
7200  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7201  * @singleton
7202  */
7203 Roo.ComponentMgr = function(){
7204     var all = new Roo.util.MixedCollection();
7205
7206     return {
7207         /**
7208          * Registers a component.
7209          * @param {Roo.Component} c The component
7210          */
7211         register : function(c){
7212             all.add(c);
7213         },
7214
7215         /**
7216          * Unregisters a component.
7217          * @param {Roo.Component} c The component
7218          */
7219         unregister : function(c){
7220             all.remove(c);
7221         },
7222
7223         /**
7224          * Returns a component by id
7225          * @param {String} id The component id
7226          */
7227         get : function(id){
7228             return all.get(id);
7229         },
7230
7231         /**
7232          * Registers a function that will be called when a specified component is added to ComponentMgr
7233          * @param {String} id The component id
7234          * @param {Funtction} fn The callback function
7235          * @param {Object} scope The scope of the callback
7236          */
7237         onAvailable : function(id, fn, scope){
7238             all.on("add", function(index, o){
7239                 if(o.id == id){
7240                     fn.call(scope || o, o);
7241                     all.un("add", fn, scope);
7242                 }
7243             });
7244         }
7245     };
7246 }();/*
7247  * Based on:
7248  * Ext JS Library 1.1.1
7249  * Copyright(c) 2006-2007, Ext JS, LLC.
7250  *
7251  * Originally Released Under LGPL - original licence link has changed is not relivant.
7252  *
7253  * Fork - LGPL
7254  * <script type="text/javascript">
7255  */
7256  
7257 /**
7258  * @class Roo.Component
7259  * @extends Roo.util.Observable
7260  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7261  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7262  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7263  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7264  * All visual components (widgets) that require rendering into a layout should subclass Component.
7265  * @constructor
7266  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7267  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7268  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7269  */
7270 Roo.Component = function(config){
7271     config = config || {};
7272     if(config.tagName || config.dom || typeof config == "string"){ // element object
7273         config = {el: config, id: config.id || config};
7274     }
7275     this.initialConfig = config;
7276
7277     Roo.apply(this, config);
7278     this.addEvents({
7279         /**
7280          * @event disable
7281          * Fires after the component is disabled.
7282              * @param {Roo.Component} this
7283              */
7284         disable : true,
7285         /**
7286          * @event enable
7287          * Fires after the component is enabled.
7288              * @param {Roo.Component} this
7289              */
7290         enable : true,
7291         /**
7292          * @event beforeshow
7293          * Fires before the component is shown.  Return false to stop the show.
7294              * @param {Roo.Component} this
7295              */
7296         beforeshow : true,
7297         /**
7298          * @event show
7299          * Fires after the component is shown.
7300              * @param {Roo.Component} this
7301              */
7302         show : true,
7303         /**
7304          * @event beforehide
7305          * Fires before the component is hidden. Return false to stop the hide.
7306              * @param {Roo.Component} this
7307              */
7308         beforehide : true,
7309         /**
7310          * @event hide
7311          * Fires after the component is hidden.
7312              * @param {Roo.Component} this
7313              */
7314         hide : true,
7315         /**
7316          * @event beforerender
7317          * Fires before the component is rendered. Return false to stop the render.
7318              * @param {Roo.Component} this
7319              */
7320         beforerender : true,
7321         /**
7322          * @event render
7323          * Fires after the component is rendered.
7324              * @param {Roo.Component} this
7325              */
7326         render : true,
7327         /**
7328          * @event beforedestroy
7329          * Fires before the component is destroyed. Return false to stop the destroy.
7330              * @param {Roo.Component} this
7331              */
7332         beforedestroy : true,
7333         /**
7334          * @event destroy
7335          * Fires after the component is destroyed.
7336              * @param {Roo.Component} this
7337              */
7338         destroy : true
7339     });
7340     if(!this.id){
7341         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7342     }
7343     Roo.ComponentMgr.register(this);
7344     Roo.Component.superclass.constructor.call(this);
7345     this.initComponent();
7346     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7347         this.render(this.renderTo);
7348         delete this.renderTo;
7349     }
7350 };
7351
7352 // private
7353 Roo.Component.AUTO_ID = 1000;
7354
7355 Roo.extend(Roo.Component, Roo.util.Observable, {
7356     /**
7357      * @property {Boolean} hidden
7358      * true if this component is hidden. Read-only.
7359      */
7360     hidden : false,
7361     /**
7362      * true if this component is disabled. Read-only.
7363      */
7364     disabled : false,
7365     /**
7366      * true if this component has been rendered. Read-only.
7367      */
7368     rendered : false,
7369     
7370     /** @cfg {String} disableClass
7371      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7372      */
7373     disabledClass : "x-item-disabled",
7374         /** @cfg {Boolean} allowDomMove
7375          * Whether the component can move the Dom node when rendering (defaults to true).
7376          */
7377     allowDomMove : true,
7378     /** @cfg {String} hideMode
7379      * How this component should hidden. Supported values are
7380      * "visibility" (css visibility), "offsets" (negative offset position) and
7381      * "display" (css display) - defaults to "display".
7382      */
7383     hideMode: 'display',
7384
7385     // private
7386     ctype : "Roo.Component",
7387
7388     /** @cfg {String} actionMode 
7389      * which property holds the element that used for  hide() / show() / disable() / enable()
7390      * default is 'el' 
7391      */
7392     actionMode : "el",
7393
7394     // private
7395     getActionEl : function(){
7396         return this[this.actionMode];
7397     },
7398
7399     initComponent : Roo.emptyFn,
7400     /**
7401      * If this is a lazy rendering component, render it to its container element.
7402      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7403      */
7404     render : function(container, position){
7405         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7406             if(!container && this.el){
7407                 this.el = Roo.get(this.el);
7408                 container = this.el.dom.parentNode;
7409                 this.allowDomMove = false;
7410             }
7411             this.container = Roo.get(container);
7412             this.rendered = true;
7413             if(position !== undefined){
7414                 if(typeof position == 'number'){
7415                     position = this.container.dom.childNodes[position];
7416                 }else{
7417                     position = Roo.getDom(position);
7418                 }
7419             }
7420             this.onRender(this.container, position || null);
7421             if(this.cls){
7422                 this.el.addClass(this.cls);
7423                 delete this.cls;
7424             }
7425             if(this.style){
7426                 this.el.applyStyles(this.style);
7427                 delete this.style;
7428             }
7429             this.fireEvent("render", this);
7430             this.afterRender(this.container);
7431             if(this.hidden){
7432                 this.hide();
7433             }
7434             if(this.disabled){
7435                 this.disable();
7436             }
7437         }
7438         return this;
7439     },
7440
7441     // private
7442     // default function is not really useful
7443     onRender : function(ct, position){
7444         if(this.el){
7445             this.el = Roo.get(this.el);
7446             if(this.allowDomMove !== false){
7447                 ct.dom.insertBefore(this.el.dom, position);
7448             }
7449         }
7450     },
7451
7452     // private
7453     getAutoCreate : function(){
7454         var cfg = typeof this.autoCreate == "object" ?
7455                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7456         if(this.id && !cfg.id){
7457             cfg.id = this.id;
7458         }
7459         return cfg;
7460     },
7461
7462     // private
7463     afterRender : Roo.emptyFn,
7464
7465     /**
7466      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7467      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7468      */
7469     destroy : function(){
7470         if(this.fireEvent("beforedestroy", this) !== false){
7471             this.purgeListeners();
7472             this.beforeDestroy();
7473             if(this.rendered){
7474                 this.el.removeAllListeners();
7475                 this.el.remove();
7476                 if(this.actionMode == "container"){
7477                     this.container.remove();
7478                 }
7479             }
7480             this.onDestroy();
7481             Roo.ComponentMgr.unregister(this);
7482             this.fireEvent("destroy", this);
7483         }
7484     },
7485
7486         // private
7487     beforeDestroy : function(){
7488
7489     },
7490
7491         // private
7492         onDestroy : function(){
7493
7494     },
7495
7496     /**
7497      * Returns the underlying {@link Roo.Element}.
7498      * @return {Roo.Element} The element
7499      */
7500     getEl : function(){
7501         return this.el;
7502     },
7503
7504     /**
7505      * Returns the id of this component.
7506      * @return {String}
7507      */
7508     getId : function(){
7509         return this.id;
7510     },
7511
7512     /**
7513      * Try to focus this component.
7514      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7515      * @return {Roo.Component} this
7516      */
7517     focus : function(selectText){
7518         if(this.rendered){
7519             this.el.focus();
7520             if(selectText === true){
7521                 this.el.dom.select();
7522             }
7523         }
7524         return this;
7525     },
7526
7527     // private
7528     blur : function(){
7529         if(this.rendered){
7530             this.el.blur();
7531         }
7532         return this;
7533     },
7534
7535     /**
7536      * Disable this component.
7537      * @return {Roo.Component} this
7538      */
7539     disable : function(){
7540         if(this.rendered){
7541             this.onDisable();
7542         }
7543         this.disabled = true;
7544         this.fireEvent("disable", this);
7545         return this;
7546     },
7547
7548         // private
7549     onDisable : function(){
7550         this.getActionEl().addClass(this.disabledClass);
7551         this.el.dom.disabled = true;
7552     },
7553
7554     /**
7555      * Enable this component.
7556      * @return {Roo.Component} this
7557      */
7558     enable : function(){
7559         if(this.rendered){
7560             this.onEnable();
7561         }
7562         this.disabled = false;
7563         this.fireEvent("enable", this);
7564         return this;
7565     },
7566
7567         // private
7568     onEnable : function(){
7569         this.getActionEl().removeClass(this.disabledClass);
7570         this.el.dom.disabled = false;
7571     },
7572
7573     /**
7574      * Convenience function for setting disabled/enabled by boolean.
7575      * @param {Boolean} disabled
7576      */
7577     setDisabled : function(disabled){
7578         this[disabled ? "disable" : "enable"]();
7579     },
7580
7581     /**
7582      * Show this component.
7583      * @return {Roo.Component} this
7584      */
7585     show: function(){
7586         if(this.fireEvent("beforeshow", this) !== false){
7587             this.hidden = false;
7588             if(this.rendered){
7589                 this.onShow();
7590             }
7591             this.fireEvent("show", this);
7592         }
7593         return this;
7594     },
7595
7596     // private
7597     onShow : function(){
7598         var ae = this.getActionEl();
7599         if(this.hideMode == 'visibility'){
7600             ae.dom.style.visibility = "visible";
7601         }else if(this.hideMode == 'offsets'){
7602             ae.removeClass('x-hidden');
7603         }else{
7604             ae.dom.style.display = "";
7605         }
7606     },
7607
7608     /**
7609      * Hide this component.
7610      * @return {Roo.Component} this
7611      */
7612     hide: function(){
7613         if(this.fireEvent("beforehide", this) !== false){
7614             this.hidden = true;
7615             if(this.rendered){
7616                 this.onHide();
7617             }
7618             this.fireEvent("hide", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onHide : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "hidden";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.addClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "none";
7632         }
7633     },
7634
7635     /**
7636      * Convenience function to hide or show this component by boolean.
7637      * @param {Boolean} visible True to show, false to hide
7638      * @return {Roo.Component} this
7639      */
7640     setVisible: function(visible){
7641         if(visible) {
7642             this.show();
7643         }else{
7644             this.hide();
7645         }
7646         return this;
7647     },
7648
7649     /**
7650      * Returns true if this component is visible.
7651      */
7652     isVisible : function(){
7653         return this.getActionEl().isVisible();
7654     },
7655
7656     cloneConfig : function(overrides){
7657         overrides = overrides || {};
7658         var id = overrides.id || Roo.id();
7659         var cfg = Roo.applyIf(overrides, this.initialConfig);
7660         cfg.id = id; // prevent dup id
7661         return new this.constructor(cfg);
7662     }
7663 });/*
7664  * Based on:
7665  * Ext JS Library 1.1.1
7666  * Copyright(c) 2006-2007, Ext JS, LLC.
7667  *
7668  * Originally Released Under LGPL - original licence link has changed is not relivant.
7669  *
7670  * Fork - LGPL
7671  * <script type="text/javascript">
7672  */
7673  (function(){ 
7674 /**
7675  * @class Roo.Layer
7676  * @extends Roo.Element
7677  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7678  * automatic maintaining of shadow/shim positions.
7679  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7680  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7681  * you can pass a string with a CSS class name. False turns off the shadow.
7682  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7683  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7684  * @cfg {String} cls CSS class to add to the element
7685  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7686  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7687  * @constructor
7688  * @param {Object} config An object with config options.
7689  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7690  */
7691
7692 Roo.Layer = function(config, existingEl){
7693     config = config || {};
7694     var dh = Roo.DomHelper;
7695     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7696     if(existingEl){
7697         this.dom = Roo.getDom(existingEl);
7698     }
7699     if(!this.dom){
7700         var o = config.dh || {tag: "div", cls: "x-layer"};
7701         this.dom = dh.append(pel, o);
7702     }
7703     if(config.cls){
7704         this.addClass(config.cls);
7705     }
7706     this.constrain = config.constrain !== false;
7707     this.visibilityMode = Roo.Element.VISIBILITY;
7708     if(config.id){
7709         this.id = this.dom.id = config.id;
7710     }else{
7711         this.id = Roo.id(this.dom);
7712     }
7713     this.zindex = config.zindex || this.getZIndex();
7714     this.position("absolute", this.zindex);
7715     if(config.shadow){
7716         this.shadowOffset = config.shadowOffset || 4;
7717         this.shadow = new Roo.Shadow({
7718             offset : this.shadowOffset,
7719             mode : config.shadow
7720         });
7721     }else{
7722         this.shadowOffset = 0;
7723     }
7724     this.useShim = config.shim !== false && Roo.useShims;
7725     this.useDisplay = config.useDisplay;
7726     this.hide();
7727 };
7728
7729 var supr = Roo.Element.prototype;
7730
7731 // shims are shared among layer to keep from having 100 iframes
7732 var shims = [];
7733
7734 Roo.extend(Roo.Layer, Roo.Element, {
7735
7736     getZIndex : function(){
7737         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7738     },
7739
7740     getShim : function(){
7741         if(!this.useShim){
7742             return null;
7743         }
7744         if(this.shim){
7745             return this.shim;
7746         }
7747         var shim = shims.shift();
7748         if(!shim){
7749             shim = this.createShim();
7750             shim.enableDisplayMode('block');
7751             shim.dom.style.display = 'none';
7752             shim.dom.style.visibility = 'visible';
7753         }
7754         var pn = this.dom.parentNode;
7755         if(shim.dom.parentNode != pn){
7756             pn.insertBefore(shim.dom, this.dom);
7757         }
7758         shim.setStyle('z-index', this.getZIndex()-2);
7759         this.shim = shim;
7760         return shim;
7761     },
7762
7763     hideShim : function(){
7764         if(this.shim){
7765             this.shim.setDisplayed(false);
7766             shims.push(this.shim);
7767             delete this.shim;
7768         }
7769     },
7770
7771     disableShadow : function(){
7772         if(this.shadow){
7773             this.shadowDisabled = true;
7774             this.shadow.hide();
7775             this.lastShadowOffset = this.shadowOffset;
7776             this.shadowOffset = 0;
7777         }
7778     },
7779
7780     enableShadow : function(show){
7781         if(this.shadow){
7782             this.shadowDisabled = false;
7783             this.shadowOffset = this.lastShadowOffset;
7784             delete this.lastShadowOffset;
7785             if(show){
7786                 this.sync(true);
7787             }
7788         }
7789     },
7790
7791     // private
7792     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7793     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7794     sync : function(doShow){
7795         var sw = this.shadow;
7796         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7797             var sh = this.getShim();
7798
7799             var w = this.getWidth(),
7800                 h = this.getHeight();
7801
7802             var l = this.getLeft(true),
7803                 t = this.getTop(true);
7804
7805             if(sw && !this.shadowDisabled){
7806                 if(doShow && !sw.isVisible()){
7807                     sw.show(this);
7808                 }else{
7809                     sw.realign(l, t, w, h);
7810                 }
7811                 if(sh){
7812                     if(doShow){
7813                        sh.show();
7814                     }
7815                     // fit the shim behind the shadow, so it is shimmed too
7816                     var a = sw.adjusts, s = sh.dom.style;
7817                     s.left = (Math.min(l, l+a.l))+"px";
7818                     s.top = (Math.min(t, t+a.t))+"px";
7819                     s.width = (w+a.w)+"px";
7820                     s.height = (h+a.h)+"px";
7821                 }
7822             }else if(sh){
7823                 if(doShow){
7824                    sh.show();
7825                 }
7826                 sh.setSize(w, h);
7827                 sh.setLeftTop(l, t);
7828             }
7829             
7830         }
7831     },
7832
7833     // private
7834     destroy : function(){
7835         this.hideShim();
7836         if(this.shadow){
7837             this.shadow.hide();
7838         }
7839         this.removeAllListeners();
7840         var pn = this.dom.parentNode;
7841         if(pn){
7842             pn.removeChild(this.dom);
7843         }
7844         Roo.Element.uncache(this.id);
7845     },
7846
7847     remove : function(){
7848         this.destroy();
7849     },
7850
7851     // private
7852     beginUpdate : function(){
7853         this.updating = true;
7854     },
7855
7856     // private
7857     endUpdate : function(){
7858         this.updating = false;
7859         this.sync(true);
7860     },
7861
7862     // private
7863     hideUnders : function(negOffset){
7864         if(this.shadow){
7865             this.shadow.hide();
7866         }
7867         this.hideShim();
7868     },
7869
7870     // private
7871     constrainXY : function(){
7872         if(this.constrain){
7873             var vw = Roo.lib.Dom.getViewWidth(),
7874                 vh = Roo.lib.Dom.getViewHeight();
7875             var s = Roo.get(document).getScroll();
7876
7877             var xy = this.getXY();
7878             var x = xy[0], y = xy[1];   
7879             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7880             // only move it if it needs it
7881             var moved = false;
7882             // first validate right/bottom
7883             if((x + w) > vw+s.left){
7884                 x = vw - w - this.shadowOffset;
7885                 moved = true;
7886             }
7887             if((y + h) > vh+s.top){
7888                 y = vh - h - this.shadowOffset;
7889                 moved = true;
7890             }
7891             // then make sure top/left isn't negative
7892             if(x < s.left){
7893                 x = s.left;
7894                 moved = true;
7895             }
7896             if(y < s.top){
7897                 y = s.top;
7898                 moved = true;
7899             }
7900             if(moved){
7901                 if(this.avoidY){
7902                     var ay = this.avoidY;
7903                     if(y <= ay && (y+h) >= ay){
7904                         y = ay-h-5;   
7905                     }
7906                 }
7907                 xy = [x, y];
7908                 this.storeXY(xy);
7909                 supr.setXY.call(this, xy);
7910                 this.sync();
7911             }
7912         }
7913     },
7914
7915     isVisible : function(){
7916         return this.visible;    
7917     },
7918
7919     // private
7920     showAction : function(){
7921         this.visible = true; // track visibility to prevent getStyle calls
7922         if(this.useDisplay === true){
7923             this.setDisplayed("");
7924         }else if(this.lastXY){
7925             supr.setXY.call(this, this.lastXY);
7926         }else if(this.lastLT){
7927             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7928         }
7929     },
7930
7931     // private
7932     hideAction : function(){
7933         this.visible = false;
7934         if(this.useDisplay === true){
7935             this.setDisplayed(false);
7936         }else{
7937             this.setLeftTop(-10000,-10000);
7938         }
7939     },
7940
7941     // overridden Element method
7942     setVisible : function(v, a, d, c, e){
7943         if(v){
7944             this.showAction();
7945         }
7946         if(a && v){
7947             var cb = function(){
7948                 this.sync(true);
7949                 if(c){
7950                     c();
7951                 }
7952             }.createDelegate(this);
7953             supr.setVisible.call(this, true, true, d, cb, e);
7954         }else{
7955             if(!v){
7956                 this.hideUnders(true);
7957             }
7958             var cb = c;
7959             if(a){
7960                 cb = function(){
7961                     this.hideAction();
7962                     if(c){
7963                         c();
7964                     }
7965                 }.createDelegate(this);
7966             }
7967             supr.setVisible.call(this, v, a, d, cb, e);
7968             if(v){
7969                 this.sync(true);
7970             }else if(!a){
7971                 this.hideAction();
7972             }
7973         }
7974     },
7975
7976     storeXY : function(xy){
7977         delete this.lastLT;
7978         this.lastXY = xy;
7979     },
7980
7981     storeLeftTop : function(left, top){
7982         delete this.lastXY;
7983         this.lastLT = [left, top];
7984     },
7985
7986     // private
7987     beforeFx : function(){
7988         this.beforeAction();
7989         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7990     },
7991
7992     // private
7993     afterFx : function(){
7994         Roo.Layer.superclass.afterFx.apply(this, arguments);
7995         this.sync(this.isVisible());
7996     },
7997
7998     // private
7999     beforeAction : function(){
8000         if(!this.updating && this.shadow){
8001             this.shadow.hide();
8002         }
8003     },
8004
8005     // overridden Element method
8006     setLeft : function(left){
8007         this.storeLeftTop(left, this.getTop(true));
8008         supr.setLeft.apply(this, arguments);
8009         this.sync();
8010     },
8011
8012     setTop : function(top){
8013         this.storeLeftTop(this.getLeft(true), top);
8014         supr.setTop.apply(this, arguments);
8015         this.sync();
8016     },
8017
8018     setLeftTop : function(left, top){
8019         this.storeLeftTop(left, top);
8020         supr.setLeftTop.apply(this, arguments);
8021         this.sync();
8022     },
8023
8024     setXY : function(xy, a, d, c, e){
8025         this.fixDisplay();
8026         this.beforeAction();
8027         this.storeXY(xy);
8028         var cb = this.createCB(c);
8029         supr.setXY.call(this, xy, a, d, cb, e);
8030         if(!a){
8031             cb();
8032         }
8033     },
8034
8035     // private
8036     createCB : function(c){
8037         var el = this;
8038         return function(){
8039             el.constrainXY();
8040             el.sync(true);
8041             if(c){
8042                 c();
8043             }
8044         };
8045     },
8046
8047     // overridden Element method
8048     setX : function(x, a, d, c, e){
8049         this.setXY([x, this.getY()], a, d, c, e);
8050     },
8051
8052     // overridden Element method
8053     setY : function(y, a, d, c, e){
8054         this.setXY([this.getX(), y], a, d, c, e);
8055     },
8056
8057     // overridden Element method
8058     setSize : function(w, h, a, d, c, e){
8059         this.beforeAction();
8060         var cb = this.createCB(c);
8061         supr.setSize.call(this, w, h, a, d, cb, e);
8062         if(!a){
8063             cb();
8064         }
8065     },
8066
8067     // overridden Element method
8068     setWidth : function(w, a, d, c, e){
8069         this.beforeAction();
8070         var cb = this.createCB(c);
8071         supr.setWidth.call(this, w, a, d, cb, e);
8072         if(!a){
8073             cb();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setHeight : function(h, a, d, c, e){
8079         this.beforeAction();
8080         var cb = this.createCB(c);
8081         supr.setHeight.call(this, h, a, d, cb, e);
8082         if(!a){
8083             cb();
8084         }
8085     },
8086
8087     // overridden Element method
8088     setBounds : function(x, y, w, h, a, d, c, e){
8089         this.beforeAction();
8090         var cb = this.createCB(c);
8091         if(!a){
8092             this.storeXY([x, y]);
8093             supr.setXY.call(this, [x, y]);
8094             supr.setSize.call(this, w, h, a, d, cb, e);
8095             cb();
8096         }else{
8097             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8098         }
8099         return this;
8100     },
8101     
8102     /**
8103      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8104      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8105      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8106      * @param {Number} zindex The new z-index to set
8107      * @return {this} The Layer
8108      */
8109     setZIndex : function(zindex){
8110         this.zindex = zindex;
8111         this.setStyle("z-index", zindex + 2);
8112         if(this.shadow){
8113             this.shadow.setZIndex(zindex + 1);
8114         }
8115         if(this.shim){
8116             this.shim.setStyle("z-index", zindex);
8117         }
8118     }
8119 });
8120 })();/*
8121  * Based on:
8122  * Ext JS Library 1.1.1
8123  * Copyright(c) 2006-2007, Ext JS, LLC.
8124  *
8125  * Originally Released Under LGPL - original licence link has changed is not relivant.
8126  *
8127  * Fork - LGPL
8128  * <script type="text/javascript">
8129  */
8130
8131
8132 /**
8133  * @class Roo.Shadow
8134  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8135  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8136  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8137  * @constructor
8138  * Create a new Shadow
8139  * @param {Object} config The config object
8140  */
8141 Roo.Shadow = function(config){
8142     Roo.apply(this, config);
8143     if(typeof this.mode != "string"){
8144         this.mode = this.defaultMode;
8145     }
8146     var o = this.offset, a = {h: 0};
8147     var rad = Math.floor(this.offset/2);
8148     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8149         case "drop":
8150             a.w = 0;
8151             a.l = a.t = o;
8152             a.t -= 1;
8153             if(Roo.isIE){
8154                 a.l -= this.offset + rad;
8155                 a.t -= this.offset + rad;
8156                 a.w -= rad;
8157                 a.h -= rad;
8158                 a.t += 1;
8159             }
8160         break;
8161         case "sides":
8162             a.w = (o*2);
8163             a.l = -o;
8164             a.t = o-1;
8165             if(Roo.isIE){
8166                 a.l -= (this.offset - rad);
8167                 a.t -= this.offset + rad;
8168                 a.l += 1;
8169                 a.w -= (this.offset - rad)*2;
8170                 a.w -= rad + 1;
8171                 a.h -= 1;
8172             }
8173         break;
8174         case "frame":
8175             a.w = a.h = (o*2);
8176             a.l = a.t = -o;
8177             a.t += 1;
8178             a.h -= 2;
8179             if(Roo.isIE){
8180                 a.l -= (this.offset - rad);
8181                 a.t -= (this.offset - rad);
8182                 a.l += 1;
8183                 a.w -= (this.offset + rad + 1);
8184                 a.h -= (this.offset + rad);
8185                 a.h += 1;
8186             }
8187         break;
8188     };
8189
8190     this.adjusts = a;
8191 };
8192
8193 Roo.Shadow.prototype = {
8194     /**
8195      * @cfg {String} mode
8196      * The shadow display mode.  Supports the following options:<br />
8197      * sides: Shadow displays on both sides and bottom only<br />
8198      * frame: Shadow displays equally on all four sides<br />
8199      * drop: Traditional bottom-right drop shadow (default)
8200      */
8201     /**
8202      * @cfg {String} offset
8203      * The number of pixels to offset the shadow from the element (defaults to 4)
8204      */
8205     offset: 4,
8206
8207     // private
8208     defaultMode: "drop",
8209
8210     /**
8211      * Displays the shadow under the target element
8212      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8213      */
8214     show : function(target){
8215         target = Roo.get(target);
8216         if(!this.el){
8217             this.el = Roo.Shadow.Pool.pull();
8218             if(this.el.dom.nextSibling != target.dom){
8219                 this.el.insertBefore(target);
8220             }
8221         }
8222         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8223         if(Roo.isIE){
8224             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8225         }
8226         this.realign(
8227             target.getLeft(true),
8228             target.getTop(true),
8229             target.getWidth(),
8230             target.getHeight()
8231         );
8232         this.el.dom.style.display = "block";
8233     },
8234
8235     /**
8236      * Returns true if the shadow is visible, else false
8237      */
8238     isVisible : function(){
8239         return this.el ? true : false;  
8240     },
8241
8242     /**
8243      * Direct alignment when values are already available. Show must be called at least once before
8244      * calling this method to ensure it is initialized.
8245      * @param {Number} left The target element left position
8246      * @param {Number} top The target element top position
8247      * @param {Number} width The target element width
8248      * @param {Number} height The target element height
8249      */
8250     realign : function(l, t, w, h){
8251         if(!this.el){
8252             return;
8253         }
8254         var a = this.adjusts, d = this.el.dom, s = d.style;
8255         var iea = 0;
8256         s.left = (l+a.l)+"px";
8257         s.top = (t+a.t)+"px";
8258         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8259  
8260         if(s.width != sws || s.height != shs){
8261             s.width = sws;
8262             s.height = shs;
8263             if(!Roo.isIE){
8264                 var cn = d.childNodes;
8265                 var sww = Math.max(0, (sw-12))+"px";
8266                 cn[0].childNodes[1].style.width = sww;
8267                 cn[1].childNodes[1].style.width = sww;
8268                 cn[2].childNodes[1].style.width = sww;
8269                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8270             }
8271         }
8272     },
8273
8274     /**
8275      * Hides this shadow
8276      */
8277     hide : function(){
8278         if(this.el){
8279             this.el.dom.style.display = "none";
8280             Roo.Shadow.Pool.push(this.el);
8281             delete this.el;
8282         }
8283     },
8284
8285     /**
8286      * Adjust the z-index of this shadow
8287      * @param {Number} zindex The new z-index
8288      */
8289     setZIndex : function(z){
8290         this.zIndex = z;
8291         if(this.el){
8292             this.el.setStyle("z-index", z);
8293         }
8294     }
8295 };
8296
8297 // Private utility class that manages the internal Shadow cache
8298 Roo.Shadow.Pool = function(){
8299     var p = [];
8300     var markup = Roo.isIE ?
8301                  '<div class="x-ie-shadow"></div>' :
8302                  '<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>';
8303     return {
8304         pull : function(){
8305             var sh = p.shift();
8306             if(!sh){
8307                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8308                 sh.autoBoxAdjust = false;
8309             }
8310             return sh;
8311         },
8312
8313         push : function(sh){
8314             p.push(sh);
8315         }
8316     };
8317 }();/*
8318  * Based on:
8319  * Ext JS Library 1.1.1
8320  * Copyright(c) 2006-2007, Ext JS, LLC.
8321  *
8322  * Originally Released Under LGPL - original licence link has changed is not relivant.
8323  *
8324  * Fork - LGPL
8325  * <script type="text/javascript">
8326  */
8327
8328 /**
8329  * @class Roo.BoxComponent
8330  * @extends Roo.Component
8331  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8332  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8333  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8334  * layout containers.
8335  * @constructor
8336  * @param {Roo.Element/String/Object} config The configuration options.
8337  */
8338 Roo.BoxComponent = function(config){
8339     Roo.Component.call(this, config);
8340     this.addEvents({
8341         /**
8342          * @event resize
8343          * Fires after the component is resized.
8344              * @param {Roo.Component} this
8345              * @param {Number} adjWidth The box-adjusted width that was set
8346              * @param {Number} adjHeight The box-adjusted height that was set
8347              * @param {Number} rawWidth The width that was originally specified
8348              * @param {Number} rawHeight The height that was originally specified
8349              */
8350         resize : true,
8351         /**
8352          * @event move
8353          * Fires after the component is moved.
8354              * @param {Roo.Component} this
8355              * @param {Number} x The new x position
8356              * @param {Number} y The new y position
8357              */
8358         move : true
8359     });
8360 };
8361
8362 Roo.extend(Roo.BoxComponent, Roo.Component, {
8363     // private, set in afterRender to signify that the component has been rendered
8364     boxReady : false,
8365     // private, used to defer height settings to subclasses
8366     deferHeight: false,
8367     /** @cfg {Number} width
8368      * width (optional) size of component
8369      */
8370      /** @cfg {Number} height
8371      * height (optional) size of component
8372      */
8373      
8374     /**
8375      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8376      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8377      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8378      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8379      * @return {Roo.BoxComponent} this
8380      */
8381     setSize : function(w, h){
8382         // support for standard size objects
8383         if(typeof w == 'object'){
8384             h = w.height;
8385             w = w.width;
8386         }
8387         // not rendered
8388         if(!this.boxReady){
8389             this.width = w;
8390             this.height = h;
8391             return this;
8392         }
8393
8394         // prevent recalcs when not needed
8395         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8396             return this;
8397         }
8398         this.lastSize = {width: w, height: h};
8399
8400         var adj = this.adjustSize(w, h);
8401         var aw = adj.width, ah = adj.height;
8402         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8403             var rz = this.getResizeEl();
8404             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8405                 rz.setSize(aw, ah);
8406             }else if(!this.deferHeight && ah !== undefined){
8407                 rz.setHeight(ah);
8408             }else if(aw !== undefined){
8409                 rz.setWidth(aw);
8410             }
8411             this.onResize(aw, ah, w, h);
8412             this.fireEvent('resize', this, aw, ah, w, h);
8413         }
8414         return this;
8415     },
8416
8417     /**
8418      * Gets the current size of the component's underlying element.
8419      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8420      */
8421     getSize : function(){
8422         return this.el.getSize();
8423     },
8424
8425     /**
8426      * Gets the current XY position of the component's underlying element.
8427      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8428      * @return {Array} The XY position of the element (e.g., [100, 200])
8429      */
8430     getPosition : function(local){
8431         if(local === true){
8432             return [this.el.getLeft(true), this.el.getTop(true)];
8433         }
8434         return this.xy || this.el.getXY();
8435     },
8436
8437     /**
8438      * Gets the current box measurements of the component's underlying element.
8439      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8440      * @returns {Object} box An object in the format {x, y, width, height}
8441      */
8442     getBox : function(local){
8443         var s = this.el.getSize();
8444         if(local){
8445             s.x = this.el.getLeft(true);
8446             s.y = this.el.getTop(true);
8447         }else{
8448             var xy = this.xy || this.el.getXY();
8449             s.x = xy[0];
8450             s.y = xy[1];
8451         }
8452         return s;
8453     },
8454
8455     /**
8456      * Sets the current box measurements of the component's underlying element.
8457      * @param {Object} box An object in the format {x, y, width, height}
8458      * @returns {Roo.BoxComponent} this
8459      */
8460     updateBox : function(box){
8461         this.setSize(box.width, box.height);
8462         this.setPagePosition(box.x, box.y);
8463         return this;
8464     },
8465
8466     // protected
8467     getResizeEl : function(){
8468         return this.resizeEl || this.el;
8469     },
8470
8471     // protected
8472     getPositionEl : function(){
8473         return this.positionEl || this.el;
8474     },
8475
8476     /**
8477      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8478      * This method fires the move event.
8479      * @param {Number} left The new left
8480      * @param {Number} top The new top
8481      * @returns {Roo.BoxComponent} this
8482      */
8483     setPosition : function(x, y){
8484         this.x = x;
8485         this.y = y;
8486         if(!this.boxReady){
8487             return this;
8488         }
8489         var adj = this.adjustPosition(x, y);
8490         var ax = adj.x, ay = adj.y;
8491
8492         var el = this.getPositionEl();
8493         if(ax !== undefined || ay !== undefined){
8494             if(ax !== undefined && ay !== undefined){
8495                 el.setLeftTop(ax, ay);
8496             }else if(ax !== undefined){
8497                 el.setLeft(ax);
8498             }else if(ay !== undefined){
8499                 el.setTop(ay);
8500             }
8501             this.onPosition(ax, ay);
8502             this.fireEvent('move', this, ax, ay);
8503         }
8504         return this;
8505     },
8506
8507     /**
8508      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8509      * This method fires the move event.
8510      * @param {Number} x The new x position
8511      * @param {Number} y The new y position
8512      * @returns {Roo.BoxComponent} this
8513      */
8514     setPagePosition : function(x, y){
8515         this.pageX = x;
8516         this.pageY = y;
8517         if(!this.boxReady){
8518             return;
8519         }
8520         if(x === undefined || y === undefined){ // cannot translate undefined points
8521             return;
8522         }
8523         var p = this.el.translatePoints(x, y);
8524         this.setPosition(p.left, p.top);
8525         return this;
8526     },
8527
8528     // private
8529     onRender : function(ct, position){
8530         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8531         if(this.resizeEl){
8532             this.resizeEl = Roo.get(this.resizeEl);
8533         }
8534         if(this.positionEl){
8535             this.positionEl = Roo.get(this.positionEl);
8536         }
8537     },
8538
8539     // private
8540     afterRender : function(){
8541         Roo.BoxComponent.superclass.afterRender.call(this);
8542         this.boxReady = true;
8543         this.setSize(this.width, this.height);
8544         if(this.x || this.y){
8545             this.setPosition(this.x, this.y);
8546         }
8547         if(this.pageX || this.pageY){
8548             this.setPagePosition(this.pageX, this.pageY);
8549         }
8550     },
8551
8552     /**
8553      * Force the component's size to recalculate based on the underlying element's current height and width.
8554      * @returns {Roo.BoxComponent} this
8555      */
8556     syncSize : function(){
8557         delete this.lastSize;
8558         this.setSize(this.el.getWidth(), this.el.getHeight());
8559         return this;
8560     },
8561
8562     /**
8563      * Called after the component is resized, this method is empty by default but can be implemented by any
8564      * subclass that needs to perform custom logic after a resize occurs.
8565      * @param {Number} adjWidth The box-adjusted width that was set
8566      * @param {Number} adjHeight The box-adjusted height that was set
8567      * @param {Number} rawWidth The width that was originally specified
8568      * @param {Number} rawHeight The height that was originally specified
8569      */
8570     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8571
8572     },
8573
8574     /**
8575      * Called after the component is moved, this method is empty by default but can be implemented by any
8576      * subclass that needs to perform custom logic after a move occurs.
8577      * @param {Number} x The new x position
8578      * @param {Number} y The new y position
8579      */
8580     onPosition : function(x, y){
8581
8582     },
8583
8584     // private
8585     adjustSize : function(w, h){
8586         if(this.autoWidth){
8587             w = 'auto';
8588         }
8589         if(this.autoHeight){
8590             h = 'auto';
8591         }
8592         return {width : w, height: h};
8593     },
8594
8595     // private
8596     adjustPosition : function(x, y){
8597         return {x : x, y: y};
8598     }
8599 });/*
8600  * Based on:
8601  * Ext JS Library 1.1.1
8602  * Copyright(c) 2006-2007, Ext JS, LLC.
8603  *
8604  * Originally Released Under LGPL - original licence link has changed is not relivant.
8605  *
8606  * Fork - LGPL
8607  * <script type="text/javascript">
8608  */
8609
8610
8611 /**
8612  * @class Roo.SplitBar
8613  * @extends Roo.util.Observable
8614  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8615  * <br><br>
8616  * Usage:
8617  * <pre><code>
8618 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8619                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8620 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8621 split.minSize = 100;
8622 split.maxSize = 600;
8623 split.animate = true;
8624 split.on('moved', splitterMoved);
8625 </code></pre>
8626  * @constructor
8627  * Create a new SplitBar
8628  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8629  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8630  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8631  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8632                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8633                         position of the SplitBar).
8634  */
8635 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8636     
8637     /** @private */
8638     this.el = Roo.get(dragElement, true);
8639     this.el.dom.unselectable = "on";
8640     /** @private */
8641     this.resizingEl = Roo.get(resizingElement, true);
8642
8643     /**
8644      * @private
8645      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8646      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8647      * @type Number
8648      */
8649     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8650     
8651     /**
8652      * The minimum size of the resizing element. (Defaults to 0)
8653      * @type Number
8654      */
8655     this.minSize = 0;
8656     
8657     /**
8658      * The maximum size of the resizing element. (Defaults to 2000)
8659      * @type Number
8660      */
8661     this.maxSize = 2000;
8662     
8663     /**
8664      * Whether to animate the transition to the new size
8665      * @type Boolean
8666      */
8667     this.animate = false;
8668     
8669     /**
8670      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8671      * @type Boolean
8672      */
8673     this.useShim = false;
8674     
8675     /** @private */
8676     this.shim = null;
8677     
8678     if(!existingProxy){
8679         /** @private */
8680         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8681     }else{
8682         this.proxy = Roo.get(existingProxy).dom;
8683     }
8684     /** @private */
8685     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8686     
8687     /** @private */
8688     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8689     
8690     /** @private */
8691     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8692     
8693     /** @private */
8694     this.dragSpecs = {};
8695     
8696     /**
8697      * @private The adapter to use to positon and resize elements
8698      */
8699     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8700     this.adapter.init(this);
8701     
8702     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8703         /** @private */
8704         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8705         this.el.addClass("x-splitbar-h");
8706     }else{
8707         /** @private */
8708         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8709         this.el.addClass("x-splitbar-v");
8710     }
8711     
8712     this.addEvents({
8713         /**
8714          * @event resize
8715          * Fires when the splitter is moved (alias for {@link #event-moved})
8716          * @param {Roo.SplitBar} this
8717          * @param {Number} newSize the new width or height
8718          */
8719         "resize" : true,
8720         /**
8721          * @event moved
8722          * Fires when the splitter is moved
8723          * @param {Roo.SplitBar} this
8724          * @param {Number} newSize the new width or height
8725          */
8726         "moved" : true,
8727         /**
8728          * @event beforeresize
8729          * Fires before the splitter is dragged
8730          * @param {Roo.SplitBar} this
8731          */
8732         "beforeresize" : true,
8733
8734         "beforeapply" : true
8735     });
8736
8737     Roo.util.Observable.call(this);
8738 };
8739
8740 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8741     onStartProxyDrag : function(x, y){
8742         this.fireEvent("beforeresize", this);
8743         if(!this.overlay){
8744             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8745             o.unselectable();
8746             o.enableDisplayMode("block");
8747             // all splitbars share the same overlay
8748             Roo.SplitBar.prototype.overlay = o;
8749         }
8750         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8751         this.overlay.show();
8752         Roo.get(this.proxy).setDisplayed("block");
8753         var size = this.adapter.getElementSize(this);
8754         this.activeMinSize = this.getMinimumSize();;
8755         this.activeMaxSize = this.getMaximumSize();;
8756         var c1 = size - this.activeMinSize;
8757         var c2 = Math.max(this.activeMaxSize - size, 0);
8758         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8759             this.dd.resetConstraints();
8760             this.dd.setXConstraint(
8761                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8762                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8763             );
8764             this.dd.setYConstraint(0, 0);
8765         }else{
8766             this.dd.resetConstraints();
8767             this.dd.setXConstraint(0, 0);
8768             this.dd.setYConstraint(
8769                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8770                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8771             );
8772          }
8773         this.dragSpecs.startSize = size;
8774         this.dragSpecs.startPoint = [x, y];
8775         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8776     },
8777     
8778     /** 
8779      * @private Called after the drag operation by the DDProxy
8780      */
8781     onEndProxyDrag : function(e){
8782         Roo.get(this.proxy).setDisplayed(false);
8783         var endPoint = Roo.lib.Event.getXY(e);
8784         if(this.overlay){
8785             this.overlay.hide();
8786         }
8787         var newSize;
8788         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8789             newSize = this.dragSpecs.startSize + 
8790                 (this.placement == Roo.SplitBar.LEFT ?
8791                     endPoint[0] - this.dragSpecs.startPoint[0] :
8792                     this.dragSpecs.startPoint[0] - endPoint[0]
8793                 );
8794         }else{
8795             newSize = this.dragSpecs.startSize + 
8796                 (this.placement == Roo.SplitBar.TOP ?
8797                     endPoint[1] - this.dragSpecs.startPoint[1] :
8798                     this.dragSpecs.startPoint[1] - endPoint[1]
8799                 );
8800         }
8801         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8802         if(newSize != this.dragSpecs.startSize){
8803             if(this.fireEvent('beforeapply', this, newSize) !== false){
8804                 this.adapter.setElementSize(this, newSize);
8805                 this.fireEvent("moved", this, newSize);
8806                 this.fireEvent("resize", this, newSize);
8807             }
8808         }
8809     },
8810     
8811     /**
8812      * Get the adapter this SplitBar uses
8813      * @return The adapter object
8814      */
8815     getAdapter : function(){
8816         return this.adapter;
8817     },
8818     
8819     /**
8820      * Set the adapter this SplitBar uses
8821      * @param {Object} adapter A SplitBar adapter object
8822      */
8823     setAdapter : function(adapter){
8824         this.adapter = adapter;
8825         this.adapter.init(this);
8826     },
8827     
8828     /**
8829      * Gets the minimum size for the resizing element
8830      * @return {Number} The minimum size
8831      */
8832     getMinimumSize : function(){
8833         return this.minSize;
8834     },
8835     
8836     /**
8837      * Sets the minimum size for the resizing element
8838      * @param {Number} minSize The minimum size
8839      */
8840     setMinimumSize : function(minSize){
8841         this.minSize = minSize;
8842     },
8843     
8844     /**
8845      * Gets the maximum size for the resizing element
8846      * @return {Number} The maximum size
8847      */
8848     getMaximumSize : function(){
8849         return this.maxSize;
8850     },
8851     
8852     /**
8853      * Sets the maximum size for the resizing element
8854      * @param {Number} maxSize The maximum size
8855      */
8856     setMaximumSize : function(maxSize){
8857         this.maxSize = maxSize;
8858     },
8859     
8860     /**
8861      * Sets the initialize size for the resizing element
8862      * @param {Number} size The initial size
8863      */
8864     setCurrentSize : function(size){
8865         var oldAnimate = this.animate;
8866         this.animate = false;
8867         this.adapter.setElementSize(this, size);
8868         this.animate = oldAnimate;
8869     },
8870     
8871     /**
8872      * Destroy this splitbar. 
8873      * @param {Boolean} removeEl True to remove the element
8874      */
8875     destroy : function(removeEl){
8876         if(this.shim){
8877             this.shim.remove();
8878         }
8879         this.dd.unreg();
8880         this.proxy.parentNode.removeChild(this.proxy);
8881         if(removeEl){
8882             this.el.remove();
8883         }
8884     }
8885 });
8886
8887 /**
8888  * @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.
8889  */
8890 Roo.SplitBar.createProxy = function(dir){
8891     var proxy = new Roo.Element(document.createElement("div"));
8892     proxy.unselectable();
8893     var cls = 'x-splitbar-proxy';
8894     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8895     document.body.appendChild(proxy.dom);
8896     return proxy.dom;
8897 };
8898
8899 /** 
8900  * @class Roo.SplitBar.BasicLayoutAdapter
8901  * Default Adapter. It assumes the splitter and resizing element are not positioned
8902  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8903  */
8904 Roo.SplitBar.BasicLayoutAdapter = function(){
8905 };
8906
8907 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8908     // do nothing for now
8909     init : function(s){
8910     
8911     },
8912     /**
8913      * Called before drag operations to get the current size of the resizing element. 
8914      * @param {Roo.SplitBar} s The SplitBar using this adapter
8915      */
8916      getElementSize : function(s){
8917         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8918             return s.resizingEl.getWidth();
8919         }else{
8920             return s.resizingEl.getHeight();
8921         }
8922     },
8923     
8924     /**
8925      * Called after drag operations to set the size of the resizing element.
8926      * @param {Roo.SplitBar} s The SplitBar using this adapter
8927      * @param {Number} newSize The new size to set
8928      * @param {Function} onComplete A function to be invoked when resizing is complete
8929      */
8930     setElementSize : function(s, newSize, onComplete){
8931         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8932             if(!s.animate){
8933                 s.resizingEl.setWidth(newSize);
8934                 if(onComplete){
8935                     onComplete(s, newSize);
8936                 }
8937             }else{
8938                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8939             }
8940         }else{
8941             
8942             if(!s.animate){
8943                 s.resizingEl.setHeight(newSize);
8944                 if(onComplete){
8945                     onComplete(s, newSize);
8946                 }
8947             }else{
8948                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8949             }
8950         }
8951     }
8952 };
8953
8954 /** 
8955  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8956  * @extends Roo.SplitBar.BasicLayoutAdapter
8957  * Adapter that  moves the splitter element to align with the resized sizing element. 
8958  * Used with an absolute positioned SplitBar.
8959  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8960  * document.body, make sure you assign an id to the body element.
8961  */
8962 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8963     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8964     this.container = Roo.get(container);
8965 };
8966
8967 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8968     init : function(s){
8969         this.basic.init(s);
8970     },
8971     
8972     getElementSize : function(s){
8973         return this.basic.getElementSize(s);
8974     },
8975     
8976     setElementSize : function(s, newSize, onComplete){
8977         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8978     },
8979     
8980     moveSplitter : function(s){
8981         var yes = Roo.SplitBar;
8982         switch(s.placement){
8983             case yes.LEFT:
8984                 s.el.setX(s.resizingEl.getRight());
8985                 break;
8986             case yes.RIGHT:
8987                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8988                 break;
8989             case yes.TOP:
8990                 s.el.setY(s.resizingEl.getBottom());
8991                 break;
8992             case yes.BOTTOM:
8993                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8994                 break;
8995         }
8996     }
8997 };
8998
8999 /**
9000  * Orientation constant - Create a vertical SplitBar
9001  * @static
9002  * @type Number
9003  */
9004 Roo.SplitBar.VERTICAL = 1;
9005
9006 /**
9007  * Orientation constant - Create a horizontal SplitBar
9008  * @static
9009  * @type Number
9010  */
9011 Roo.SplitBar.HORIZONTAL = 2;
9012
9013 /**
9014  * Placement constant - The resizing element is to the left of the splitter element
9015  * @static
9016  * @type Number
9017  */
9018 Roo.SplitBar.LEFT = 1;
9019
9020 /**
9021  * Placement constant - The resizing element is to the right of the splitter element
9022  * @static
9023  * @type Number
9024  */
9025 Roo.SplitBar.RIGHT = 2;
9026
9027 /**
9028  * Placement constant - The resizing element is positioned above the splitter element
9029  * @static
9030  * @type Number
9031  */
9032 Roo.SplitBar.TOP = 3;
9033
9034 /**
9035  * Placement constant - The resizing element is positioned under splitter element
9036  * @static
9037  * @type Number
9038  */
9039 Roo.SplitBar.BOTTOM = 4;
9040 /*
9041  * Based on:
9042  * Ext JS Library 1.1.1
9043  * Copyright(c) 2006-2007, Ext JS, LLC.
9044  *
9045  * Originally Released Under LGPL - original licence link has changed is not relivant.
9046  *
9047  * Fork - LGPL
9048  * <script type="text/javascript">
9049  */
9050
9051 /**
9052  * @class Roo.View
9053  * @extends Roo.util.Observable
9054  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9055  * This class also supports single and multi selection modes. <br>
9056  * Create a data model bound view:
9057  <pre><code>
9058  var store = new Roo.data.Store(...);
9059
9060  var view = new Roo.View({
9061     el : "my-element",
9062     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9063  
9064     singleSelect: true,
9065     selectedClass: "ydataview-selected",
9066     store: store
9067  });
9068
9069  // listen for node click?
9070  view.on("click", function(vw, index, node, e){
9071  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9072  });
9073
9074  // load XML data
9075  dataModel.load("foobar.xml");
9076  </code></pre>
9077  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9078  * <br><br>
9079  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9080  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9081  * 
9082  * Note: old style constructor is still suported (container, template, config)
9083  * 
9084  * @constructor
9085  * Create a new View
9086  * @param {Object} config The config object
9087  * 
9088  */
9089 Roo.View = function(config, depreciated_tpl, depreciated_config){
9090     
9091     if (typeof(depreciated_tpl) == 'undefined') {
9092         // new way.. - universal constructor.
9093         Roo.apply(this, config);
9094         this.el  = Roo.get(this.el);
9095     } else {
9096         // old format..
9097         this.el  = Roo.get(config);
9098         this.tpl = depreciated_tpl;
9099         Roo.apply(this, depreciated_config);
9100     }
9101      
9102     
9103     if(typeof(this.tpl) == "string"){
9104         this.tpl = new Roo.Template(this.tpl);
9105     } else {
9106         // support xtype ctors..
9107         this.tpl = new Roo.factory(this.tpl, Roo);
9108     }
9109     
9110     
9111     this.tpl.compile();
9112    
9113
9114      
9115     /** @private */
9116     this.addEvents({
9117     /**
9118      * @event beforeclick
9119      * Fires before a click is processed. Returns false to cancel the default action.
9120      * @param {Roo.View} this
9121      * @param {Number} index The index of the target node
9122      * @param {HTMLElement} node The target node
9123      * @param {Roo.EventObject} e The raw event object
9124      */
9125         "beforeclick" : true,
9126     /**
9127      * @event click
9128      * Fires when a template node is clicked.
9129      * @param {Roo.View} this
9130      * @param {Number} index The index of the target node
9131      * @param {HTMLElement} node The target node
9132      * @param {Roo.EventObject} e The raw event object
9133      */
9134         "click" : true,
9135     /**
9136      * @event dblclick
9137      * Fires when a template node is double clicked.
9138      * @param {Roo.View} this
9139      * @param {Number} index The index of the target node
9140      * @param {HTMLElement} node The target node
9141      * @param {Roo.EventObject} e The raw event object
9142      */
9143         "dblclick" : true,
9144     /**
9145      * @event contextmenu
9146      * Fires when a template node is right clicked.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "contextmenu" : true,
9153     /**
9154      * @event selectionchange
9155      * Fires when the selected nodes change.
9156      * @param {Roo.View} this
9157      * @param {Array} selections Array of the selected nodes
9158      */
9159         "selectionchange" : true,
9160
9161     /**
9162      * @event beforeselect
9163      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9164      * @param {Roo.View} this
9165      * @param {HTMLElement} node The node to be selected
9166      * @param {Array} selections Array of currently selected nodes
9167      */
9168         "beforeselect" : true
9169     });
9170
9171     this.el.on({
9172         "click": this.onClick,
9173         "dblclick": this.onDblClick,
9174         "contextmenu": this.onContextMenu,
9175         scope:this
9176     });
9177
9178     this.selections = [];
9179     this.nodes = [];
9180     this.cmp = new Roo.CompositeElementLite([]);
9181     if(this.store){
9182         this.store = Roo.factory(this.store, Roo.data);
9183         this.setStore(this.store, true);
9184     }
9185     Roo.View.superclass.constructor.call(this);
9186 };
9187
9188 Roo.extend(Roo.View, Roo.util.Observable, {
9189     
9190      /**
9191      * @cfg {Roo.data.Store} store Data store to load data from.
9192      */
9193     store : false,
9194     
9195     /**
9196      * @cfg {String|Roo.Element} el The container element.
9197      */
9198     el : '',
9199     
9200     /**
9201      * @cfg {String|Roo.Template} tpl The template used by this View 
9202      */
9203     tpl : false,
9204     
9205     /**
9206      * @cfg {String} selectedClass The css class to add to selected nodes
9207      */
9208     selectedClass : "x-view-selected",
9209      /**
9210      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9211      */
9212     emptyText : "",
9213     /**
9214      * @cfg {Boolean} multiSelect Allow multiple selection
9215      */
9216     
9217     multiSelect : false,
9218     /**
9219      * @cfg {Boolean} singleSelect Allow single selection
9220      */
9221     singleSelect:  false,
9222     
9223     /**
9224      * Returns the element this view is bound to.
9225      * @return {Roo.Element}
9226      */
9227     getEl : function(){
9228         return this.el;
9229     },
9230
9231     /**
9232      * Refreshes the view.
9233      */
9234     refresh : function(){
9235         var t = this.tpl;
9236         this.clearSelections();
9237         this.el.update("");
9238         var html = [];
9239         var records = this.store.getRange();
9240         if(records.length < 1){
9241             this.el.update(this.emptyText);
9242             return;
9243         }
9244         for(var i = 0, len = records.length; i < len; i++){
9245             var data = this.prepareData(records[i].data, i, records[i]);
9246             html[html.length] = t.apply(data);
9247         }
9248         this.el.update(html.join(""));
9249         this.nodes = this.el.dom.childNodes;
9250         this.updateIndexes(0);
9251     },
9252
9253     /**
9254      * Function to override to reformat the data that is sent to
9255      * the template for each node.
9256      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9257      * a JSON object for an UpdateManager bound view).
9258      */
9259     prepareData : function(data){
9260         return data;
9261     },
9262
9263     onUpdate : function(ds, record){
9264         this.clearSelections();
9265         var index = this.store.indexOf(record);
9266         var n = this.nodes[index];
9267         this.tpl.insertBefore(n, this.prepareData(record.data));
9268         n.parentNode.removeChild(n);
9269         this.updateIndexes(index, index);
9270     },
9271
9272     onAdd : function(ds, records, index){
9273         this.clearSelections();
9274         if(this.nodes.length == 0){
9275             this.refresh();
9276             return;
9277         }
9278         var n = this.nodes[index];
9279         for(var i = 0, len = records.length; i < len; i++){
9280             var d = this.prepareData(records[i].data);
9281             if(n){
9282                 this.tpl.insertBefore(n, d);
9283             }else{
9284                 this.tpl.append(this.el, d);
9285             }
9286         }
9287         this.updateIndexes(index);
9288     },
9289
9290     onRemove : function(ds, record, index){
9291         this.clearSelections();
9292         this.el.dom.removeChild(this.nodes[index]);
9293         this.updateIndexes(index);
9294     },
9295
9296     /**
9297      * Refresh an individual node.
9298      * @param {Number} index
9299      */
9300     refreshNode : function(index){
9301         this.onUpdate(this.store, this.store.getAt(index));
9302     },
9303
9304     updateIndexes : function(startIndex, endIndex){
9305         var ns = this.nodes;
9306         startIndex = startIndex || 0;
9307         endIndex = endIndex || ns.length - 1;
9308         for(var i = startIndex; i <= endIndex; i++){
9309             ns[i].nodeIndex = i;
9310         }
9311     },
9312
9313     /**
9314      * Changes the data store this view uses and refresh the view.
9315      * @param {Store} store
9316      */
9317     setStore : function(store, initial){
9318         if(!initial && this.store){
9319             this.store.un("datachanged", this.refresh);
9320             this.store.un("add", this.onAdd);
9321             this.store.un("remove", this.onRemove);
9322             this.store.un("update", this.onUpdate);
9323             this.store.un("clear", this.refresh);
9324         }
9325         if(store){
9326           
9327             store.on("datachanged", this.refresh, this);
9328             store.on("add", this.onAdd, this);
9329             store.on("remove", this.onRemove, this);
9330             store.on("update", this.onUpdate, this);
9331             store.on("clear", this.refresh, this);
9332         }
9333         
9334         if(store){
9335             this.refresh();
9336         }
9337     },
9338
9339     /**
9340      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9341      * @param {HTMLElement} node
9342      * @return {HTMLElement} The template node
9343      */
9344     findItemFromChild : function(node){
9345         var el = this.el.dom;
9346         if(!node || node.parentNode == el){
9347                     return node;
9348             }
9349             var p = node.parentNode;
9350             while(p && p != el){
9351             if(p.parentNode == el){
9352                 return p;
9353             }
9354             p = p.parentNode;
9355         }
9356             return null;
9357     },
9358
9359     /** @ignore */
9360     onClick : function(e){
9361         var item = this.findItemFromChild(e.getTarget());
9362         if(item){
9363             var index = this.indexOf(item);
9364             if(this.onItemClick(item, index, e) !== false){
9365                 this.fireEvent("click", this, index, item, e);
9366             }
9367         }else{
9368             this.clearSelections();
9369         }
9370     },
9371
9372     /** @ignore */
9373     onContextMenu : function(e){
9374         var item = this.findItemFromChild(e.getTarget());
9375         if(item){
9376             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9377         }
9378     },
9379
9380     /** @ignore */
9381     onDblClick : function(e){
9382         var item = this.findItemFromChild(e.getTarget());
9383         if(item){
9384             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9385         }
9386     },
9387
9388     onItemClick : function(item, index, e){
9389         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9390             return false;
9391         }
9392         if(this.multiSelect || this.singleSelect){
9393             if(this.multiSelect && e.shiftKey && this.lastSelection){
9394                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9395             }else{
9396                 this.select(item, this.multiSelect && e.ctrlKey);
9397                 this.lastSelection = item;
9398             }
9399             e.preventDefault();
9400         }
9401         return true;
9402     },
9403
9404     /**
9405      * Get the number of selected nodes.
9406      * @return {Number}
9407      */
9408     getSelectionCount : function(){
9409         return this.selections.length;
9410     },
9411
9412     /**
9413      * Get the currently selected nodes.
9414      * @return {Array} An array of HTMLElements
9415      */
9416     getSelectedNodes : function(){
9417         return this.selections;
9418     },
9419
9420     /**
9421      * Get the indexes of the selected nodes.
9422      * @return {Array}
9423      */
9424     getSelectedIndexes : function(){
9425         var indexes = [], s = this.selections;
9426         for(var i = 0, len = s.length; i < len; i++){
9427             indexes.push(s[i].nodeIndex);
9428         }
9429         return indexes;
9430     },
9431
9432     /**
9433      * Clear all selections
9434      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9435      */
9436     clearSelections : function(suppressEvent){
9437         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9438             this.cmp.elements = this.selections;
9439             this.cmp.removeClass(this.selectedClass);
9440             this.selections = [];
9441             if(!suppressEvent){
9442                 this.fireEvent("selectionchange", this, this.selections);
9443             }
9444         }
9445     },
9446
9447     /**
9448      * Returns true if the passed node is selected
9449      * @param {HTMLElement/Number} node The node or node index
9450      * @return {Boolean}
9451      */
9452     isSelected : function(node){
9453         var s = this.selections;
9454         if(s.length < 1){
9455             return false;
9456         }
9457         node = this.getNode(node);
9458         return s.indexOf(node) !== -1;
9459     },
9460
9461     /**
9462      * Selects nodes.
9463      * @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
9464      * @param {Boolean} keepExisting (optional) true to keep existing selections
9465      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9466      */
9467     select : function(nodeInfo, keepExisting, suppressEvent){
9468         if(nodeInfo instanceof Array){
9469             if(!keepExisting){
9470                 this.clearSelections(true);
9471             }
9472             for(var i = 0, len = nodeInfo.length; i < len; i++){
9473                 this.select(nodeInfo[i], true, true);
9474             }
9475         } else{
9476             var node = this.getNode(nodeInfo);
9477             if(node && !this.isSelected(node)){
9478                 if(!keepExisting){
9479                     this.clearSelections(true);
9480                 }
9481                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9482                     Roo.fly(node).addClass(this.selectedClass);
9483                     this.selections.push(node);
9484                     if(!suppressEvent){
9485                         this.fireEvent("selectionchange", this, this.selections);
9486                     }
9487                 }
9488             }
9489         }
9490     },
9491
9492     /**
9493      * Gets a template node.
9494      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9495      * @return {HTMLElement} The node or null if it wasn't found
9496      */
9497     getNode : function(nodeInfo){
9498         if(typeof nodeInfo == "string"){
9499             return document.getElementById(nodeInfo);
9500         }else if(typeof nodeInfo == "number"){
9501             return this.nodes[nodeInfo];
9502         }
9503         return nodeInfo;
9504     },
9505
9506     /**
9507      * Gets a range template nodes.
9508      * @param {Number} startIndex
9509      * @param {Number} endIndex
9510      * @return {Array} An array of nodes
9511      */
9512     getNodes : function(start, end){
9513         var ns = this.nodes;
9514         start = start || 0;
9515         end = typeof end == "undefined" ? ns.length - 1 : end;
9516         var nodes = [];
9517         if(start <= end){
9518             for(var i = start; i <= end; i++){
9519                 nodes.push(ns[i]);
9520             }
9521         } else{
9522             for(var i = start; i >= end; i--){
9523                 nodes.push(ns[i]);
9524             }
9525         }
9526         return nodes;
9527     },
9528
9529     /**
9530      * Finds the index of the passed node
9531      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9532      * @return {Number} The index of the node or -1
9533      */
9534     indexOf : function(node){
9535         node = this.getNode(node);
9536         if(typeof node.nodeIndex == "number"){
9537             return node.nodeIndex;
9538         }
9539         var ns = this.nodes;
9540         for(var i = 0, len = ns.length; i < len; i++){
9541             if(ns[i] == node){
9542                 return i;
9543             }
9544         }
9545         return -1;
9546     }
9547 });
9548 /*
9549  * Based on:
9550  * Ext JS Library 1.1.1
9551  * Copyright(c) 2006-2007, Ext JS, LLC.
9552  *
9553  * Originally Released Under LGPL - original licence link has changed is not relivant.
9554  *
9555  * Fork - LGPL
9556  * <script type="text/javascript">
9557  */
9558
9559 /**
9560  * @class Roo.JsonView
9561  * @extends Roo.View
9562  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9563 <pre><code>
9564 var view = new Roo.JsonView({
9565     container: "my-element",
9566     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9567     multiSelect: true, 
9568     jsonRoot: "data" 
9569 });
9570
9571 // listen for node click?
9572 view.on("click", function(vw, index, node, e){
9573     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9574 });
9575
9576 // direct load of JSON data
9577 view.load("foobar.php");
9578
9579 // Example from my blog list
9580 var tpl = new Roo.Template(
9581     '&lt;div class="entry"&gt;' +
9582     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9583     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9584     "&lt;/div&gt;&lt;hr /&gt;"
9585 );
9586
9587 var moreView = new Roo.JsonView({
9588     container :  "entry-list", 
9589     template : tpl,
9590     jsonRoot: "posts"
9591 });
9592 moreView.on("beforerender", this.sortEntries, this);
9593 moreView.load({
9594     url: "/blog/get-posts.php",
9595     params: "allposts=true",
9596     text: "Loading Blog Entries..."
9597 });
9598 </code></pre>
9599
9600 * Note: old code is supported with arguments : (container, template, config)
9601
9602
9603  * @constructor
9604  * Create a new JsonView
9605  * 
9606  * @param {Object} config The config object
9607  * 
9608  */
9609 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9610     
9611     
9612     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9613
9614     var um = this.el.getUpdateManager();
9615     um.setRenderer(this);
9616     um.on("update", this.onLoad, this);
9617     um.on("failure", this.onLoadException, this);
9618
9619     /**
9620      * @event beforerender
9621      * Fires before rendering of the downloaded JSON data.
9622      * @param {Roo.JsonView} this
9623      * @param {Object} data The JSON data loaded
9624      */
9625     /**
9626      * @event load
9627      * Fires when data is loaded.
9628      * @param {Roo.JsonView} this
9629      * @param {Object} data The JSON data loaded
9630      * @param {Object} response The raw Connect response object
9631      */
9632     /**
9633      * @event loadexception
9634      * Fires when loading fails.
9635      * @param {Roo.JsonView} this
9636      * @param {Object} response The raw Connect response object
9637      */
9638     this.addEvents({
9639         'beforerender' : true,
9640         'load' : true,
9641         'loadexception' : true
9642     });
9643 };
9644 Roo.extend(Roo.JsonView, Roo.View, {
9645     /**
9646      * @type {String} The root property in the loaded JSON object that contains the data
9647      */
9648     jsonRoot : "",
9649
9650     /**
9651      * Refreshes the view.
9652      */
9653     refresh : function(){
9654         this.clearSelections();
9655         this.el.update("");
9656         var html = [];
9657         var o = this.jsonData;
9658         if(o && o.length > 0){
9659             for(var i = 0, len = o.length; i < len; i++){
9660                 var data = this.prepareData(o[i], i, o);
9661                 html[html.length] = this.tpl.apply(data);
9662             }
9663         }else{
9664             html.push(this.emptyText);
9665         }
9666         this.el.update(html.join(""));
9667         this.nodes = this.el.dom.childNodes;
9668         this.updateIndexes(0);
9669     },
9670
9671     /**
9672      * 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.
9673      * @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:
9674      <pre><code>
9675      view.load({
9676          url: "your-url.php",
9677          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9678          callback: yourFunction,
9679          scope: yourObject, //(optional scope)
9680          discardUrl: false,
9681          nocache: false,
9682          text: "Loading...",
9683          timeout: 30,
9684          scripts: false
9685      });
9686      </code></pre>
9687      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9688      * 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.
9689      * @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}
9690      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9691      * @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.
9692      */
9693     load : function(){
9694         var um = this.el.getUpdateManager();
9695         um.update.apply(um, arguments);
9696     },
9697
9698     render : function(el, response){
9699         this.clearSelections();
9700         this.el.update("");
9701         var o;
9702         try{
9703             o = Roo.util.JSON.decode(response.responseText);
9704             if(this.jsonRoot){
9705                 
9706                 o = o[this.jsonRoot];
9707             }
9708         } catch(e){
9709         }
9710         /**
9711          * The current JSON data or null
9712          */
9713         this.jsonData = o;
9714         this.beforeRender();
9715         this.refresh();
9716     },
9717
9718 /**
9719  * Get the number of records in the current JSON dataset
9720  * @return {Number}
9721  */
9722     getCount : function(){
9723         return this.jsonData ? this.jsonData.length : 0;
9724     },
9725
9726 /**
9727  * Returns the JSON object for the specified node(s)
9728  * @param {HTMLElement/Array} node The node or an array of nodes
9729  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9730  * you get the JSON object for the node
9731  */
9732     getNodeData : function(node){
9733         if(node instanceof Array){
9734             var data = [];
9735             for(var i = 0, len = node.length; i < len; i++){
9736                 data.push(this.getNodeData(node[i]));
9737             }
9738             return data;
9739         }
9740         return this.jsonData[this.indexOf(node)] || null;
9741     },
9742
9743     beforeRender : function(){
9744         this.snapshot = this.jsonData;
9745         if(this.sortInfo){
9746             this.sort.apply(this, this.sortInfo);
9747         }
9748         this.fireEvent("beforerender", this, this.jsonData);
9749     },
9750
9751     onLoad : function(el, o){
9752         this.fireEvent("load", this, this.jsonData, o);
9753     },
9754
9755     onLoadException : function(el, o){
9756         this.fireEvent("loadexception", this, o);
9757     },
9758
9759 /**
9760  * Filter the data by a specific property.
9761  * @param {String} property A property on your JSON objects
9762  * @param {String/RegExp} value Either string that the property values
9763  * should start with, or a RegExp to test against the property
9764  */
9765     filter : function(property, value){
9766         if(this.jsonData){
9767             var data = [];
9768             var ss = this.snapshot;
9769             if(typeof value == "string"){
9770                 var vlen = value.length;
9771                 if(vlen == 0){
9772                     this.clearFilter();
9773                     return;
9774                 }
9775                 value = value.toLowerCase();
9776                 for(var i = 0, len = ss.length; i < len; i++){
9777                     var o = ss[i];
9778                     if(o[property].substr(0, vlen).toLowerCase() == value){
9779                         data.push(o);
9780                     }
9781                 }
9782             } else if(value.exec){ // regex?
9783                 for(var i = 0, len = ss.length; i < len; i++){
9784                     var o = ss[i];
9785                     if(value.test(o[property])){
9786                         data.push(o);
9787                     }
9788                 }
9789             } else{
9790                 return;
9791             }
9792             this.jsonData = data;
9793             this.refresh();
9794         }
9795     },
9796
9797 /**
9798  * Filter by a function. The passed function will be called with each
9799  * object in the current dataset. If the function returns true the value is kept,
9800  * otherwise it is filtered.
9801  * @param {Function} fn
9802  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9803  */
9804     filterBy : function(fn, scope){
9805         if(this.jsonData){
9806             var data = [];
9807             var ss = this.snapshot;
9808             for(var i = 0, len = ss.length; i < len; i++){
9809                 var o = ss[i];
9810                 if(fn.call(scope || this, o)){
9811                     data.push(o);
9812                 }
9813             }
9814             this.jsonData = data;
9815             this.refresh();
9816         }
9817     },
9818
9819 /**
9820  * Clears the current filter.
9821  */
9822     clearFilter : function(){
9823         if(this.snapshot && this.jsonData != this.snapshot){
9824             this.jsonData = this.snapshot;
9825             this.refresh();
9826         }
9827     },
9828
9829
9830 /**
9831  * Sorts the data for this view and refreshes it.
9832  * @param {String} property A property on your JSON objects to sort on
9833  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9834  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9835  */
9836     sort : function(property, dir, sortType){
9837         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9838         if(this.jsonData){
9839             var p = property;
9840             var dsc = dir && dir.toLowerCase() == "desc";
9841             var f = function(o1, o2){
9842                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9843                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9844                 ;
9845                 if(v1 < v2){
9846                     return dsc ? +1 : -1;
9847                 } else if(v1 > v2){
9848                     return dsc ? -1 : +1;
9849                 } else{
9850                     return 0;
9851                 }
9852             };
9853             this.jsonData.sort(f);
9854             this.refresh();
9855             if(this.jsonData != this.snapshot){
9856                 this.snapshot.sort(f);
9857             }
9858         }
9859     }
9860 });/*
9861  * Based on:
9862  * Ext JS Library 1.1.1
9863  * Copyright(c) 2006-2007, Ext JS, LLC.
9864  *
9865  * Originally Released Under LGPL - original licence link has changed is not relivant.
9866  *
9867  * Fork - LGPL
9868  * <script type="text/javascript">
9869  */
9870  
9871
9872 /**
9873  * @class Roo.ColorPalette
9874  * @extends Roo.Component
9875  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9876  * Here's an example of typical usage:
9877  * <pre><code>
9878 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9879 cp.render('my-div');
9880
9881 cp.on('select', function(palette, selColor){
9882     // do something with selColor
9883 });
9884 </code></pre>
9885  * @constructor
9886  * Create a new ColorPalette
9887  * @param {Object} config The config object
9888  */
9889 Roo.ColorPalette = function(config){
9890     Roo.ColorPalette.superclass.constructor.call(this, config);
9891     this.addEvents({
9892         /**
9893              * @event select
9894              * Fires when a color is selected
9895              * @param {ColorPalette} this
9896              * @param {String} color The 6-digit color hex code (without the # symbol)
9897              */
9898         select: true
9899     });
9900
9901     if(this.handler){
9902         this.on("select", this.handler, this.scope, true);
9903     }
9904 };
9905 Roo.extend(Roo.ColorPalette, Roo.Component, {
9906     /**
9907      * @cfg {String} itemCls
9908      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9909      */
9910     itemCls : "x-color-palette",
9911     /**
9912      * @cfg {String} value
9913      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9914      * the hex codes are case-sensitive.
9915      */
9916     value : null,
9917     clickEvent:'click',
9918     // private
9919     ctype: "Roo.ColorPalette",
9920
9921     /**
9922      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9923      */
9924     allowReselect : false,
9925
9926     /**
9927      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9928      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9929      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9930      * of colors with the width setting until the box is symmetrical.</p>
9931      * <p>You can override individual colors if needed:</p>
9932      * <pre><code>
9933 var cp = new Roo.ColorPalette();
9934 cp.colors[0] = "FF0000";  // change the first box to red
9935 </code></pre>
9936
9937 Or you can provide a custom array of your own for complete control:
9938 <pre><code>
9939 var cp = new Roo.ColorPalette();
9940 cp.colors = ["000000", "993300", "333300"];
9941 </code></pre>
9942      * @type Array
9943      */
9944     colors : [
9945         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9946         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9947         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9948         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9949         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9950     ],
9951
9952     // private
9953     onRender : function(container, position){
9954         var t = new Roo.MasterTemplate(
9955             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9956         );
9957         var c = this.colors;
9958         for(var i = 0, len = c.length; i < len; i++){
9959             t.add([c[i]]);
9960         }
9961         var el = document.createElement("div");
9962         el.className = this.itemCls;
9963         t.overwrite(el);
9964         container.dom.insertBefore(el, position);
9965         this.el = Roo.get(el);
9966         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9967         if(this.clickEvent != 'click'){
9968             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9969         }
9970     },
9971
9972     // private
9973     afterRender : function(){
9974         Roo.ColorPalette.superclass.afterRender.call(this);
9975         if(this.value){
9976             var s = this.value;
9977             this.value = null;
9978             this.select(s);
9979         }
9980     },
9981
9982     // private
9983     handleClick : function(e, t){
9984         e.preventDefault();
9985         if(!this.disabled){
9986             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9987             this.select(c.toUpperCase());
9988         }
9989     },
9990
9991     /**
9992      * Selects the specified color in the palette (fires the select event)
9993      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9994      */
9995     select : function(color){
9996         color = color.replace("#", "");
9997         if(color != this.value || this.allowReselect){
9998             var el = this.el;
9999             if(this.value){
10000                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10001             }
10002             el.child("a.color-"+color).addClass("x-color-palette-sel");
10003             this.value = color;
10004             this.fireEvent("select", this, color);
10005         }
10006     }
10007 });/*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017  
10018 /**
10019  * @class Roo.DatePicker
10020  * @extends Roo.Component
10021  * Simple date picker class.
10022  * @constructor
10023  * Create a new DatePicker
10024  * @param {Object} config The config object
10025  */
10026 Roo.DatePicker = function(config){
10027     Roo.DatePicker.superclass.constructor.call(this, config);
10028
10029     this.value = config && config.value ?
10030                  config.value.clearTime() : new Date().clearTime();
10031
10032     this.addEvents({
10033         /**
10034              * @event select
10035              * Fires when a date is selected
10036              * @param {DatePicker} this
10037              * @param {Date} date The selected date
10038              */
10039         select: true
10040     });
10041
10042     if(this.handler){
10043         this.on("select", this.handler,  this.scope || this);
10044     }
10045     // build the disabledDatesRE
10046     if(!this.disabledDatesRE && this.disabledDates){
10047         var dd = this.disabledDates;
10048         var re = "(?:";
10049         for(var i = 0; i < dd.length; i++){
10050             re += dd[i];
10051             if(i != dd.length-1) re += "|";
10052         }
10053         this.disabledDatesRE = new RegExp(re + ")");
10054     }
10055 };
10056
10057 Roo.extend(Roo.DatePicker, Roo.Component, {
10058     /**
10059      * @cfg {String} todayText
10060      * The text to display on the button that selects the current date (defaults to "Today")
10061      */
10062     todayText : "Today",
10063     /**
10064      * @cfg {String} okText
10065      * The text to display on the ok button
10066      */
10067     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10068     /**
10069      * @cfg {String} cancelText
10070      * The text to display on the cancel button
10071      */
10072     cancelText : "Cancel",
10073     /**
10074      * @cfg {String} todayTip
10075      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10076      */
10077     todayTip : "{0} (Spacebar)",
10078     /**
10079      * @cfg {Date} minDate
10080      * Minimum allowable date (JavaScript date object, defaults to null)
10081      */
10082     minDate : null,
10083     /**
10084      * @cfg {Date} maxDate
10085      * Maximum allowable date (JavaScript date object, defaults to null)
10086      */
10087     maxDate : null,
10088     /**
10089      * @cfg {String} minText
10090      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10091      */
10092     minText : "This date is before the minimum date",
10093     /**
10094      * @cfg {String} maxText
10095      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10096      */
10097     maxText : "This date is after the maximum date",
10098     /**
10099      * @cfg {String} format
10100      * The default date format string which can be overriden for localization support.  The format must be
10101      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10102      */
10103     format : "m/d/y",
10104     /**
10105      * @cfg {Array} disabledDays
10106      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10107      */
10108     disabledDays : null,
10109     /**
10110      * @cfg {String} disabledDaysText
10111      * The tooltip to display when the date falls on a disabled day (defaults to "")
10112      */
10113     disabledDaysText : "",
10114     /**
10115      * @cfg {RegExp} disabledDatesRE
10116      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10117      */
10118     disabledDatesRE : null,
10119     /**
10120      * @cfg {String} disabledDatesText
10121      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10122      */
10123     disabledDatesText : "",
10124     /**
10125      * @cfg {Boolean} constrainToViewport
10126      * True to constrain the date picker to the viewport (defaults to true)
10127      */
10128     constrainToViewport : true,
10129     /**
10130      * @cfg {Array} monthNames
10131      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10132      */
10133     monthNames : Date.monthNames,
10134     /**
10135      * @cfg {Array} dayNames
10136      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10137      */
10138     dayNames : Date.dayNames,
10139     /**
10140      * @cfg {String} nextText
10141      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10142      */
10143     nextText: 'Next Month (Control+Right)',
10144     /**
10145      * @cfg {String} prevText
10146      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10147      */
10148     prevText: 'Previous Month (Control+Left)',
10149     /**
10150      * @cfg {String} monthYearText
10151      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10152      */
10153     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10154     /**
10155      * @cfg {Number} startDay
10156      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10157      */
10158     startDay : 0,
10159     /**
10160      * @cfg {Bool} showClear
10161      * Show a clear button (usefull for date form elements that can be blank.)
10162      */
10163     
10164     showClear: false,
10165     
10166     /**
10167      * Sets the value of the date field
10168      * @param {Date} value The date to set
10169      */
10170     setValue : function(value){
10171         var old = this.value;
10172         this.value = value.clearTime(true);
10173         if(this.el){
10174             this.update(this.value);
10175         }
10176     },
10177
10178     /**
10179      * Gets the current selected value of the date field
10180      * @return {Date} The selected date
10181      */
10182     getValue : function(){
10183         return this.value;
10184     },
10185
10186     // private
10187     focus : function(){
10188         if(this.el){
10189             this.update(this.activeDate);
10190         }
10191     },
10192
10193     // private
10194     onRender : function(container, position){
10195         var m = [
10196              '<table cellspacing="0">',
10197                 '<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>',
10198                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10199         var dn = this.dayNames;
10200         for(var i = 0; i < 7; i++){
10201             var d = this.startDay+i;
10202             if(d > 6){
10203                 d = d-7;
10204             }
10205             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10206         }
10207         m[m.length] = "</tr></thead><tbody><tr>";
10208         for(var i = 0; i < 42; i++) {
10209             if(i % 7 == 0 && i != 0){
10210                 m[m.length] = "</tr><tr>";
10211             }
10212             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10213         }
10214         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10215             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10216
10217         var el = document.createElement("div");
10218         el.className = "x-date-picker";
10219         el.innerHTML = m.join("");
10220
10221         container.dom.insertBefore(el, position);
10222
10223         this.el = Roo.get(el);
10224         this.eventEl = Roo.get(el.firstChild);
10225
10226         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10227             handler: this.showPrevMonth,
10228             scope: this,
10229             preventDefault:true,
10230             stopDefault:true
10231         });
10232
10233         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10234             handler: this.showNextMonth,
10235             scope: this,
10236             preventDefault:true,
10237             stopDefault:true
10238         });
10239
10240         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10241
10242         this.monthPicker = this.el.down('div.x-date-mp');
10243         this.monthPicker.enableDisplayMode('block');
10244         
10245         var kn = new Roo.KeyNav(this.eventEl, {
10246             "left" : function(e){
10247                 e.ctrlKey ?
10248                     this.showPrevMonth() :
10249                     this.update(this.activeDate.add("d", -1));
10250             },
10251
10252             "right" : function(e){
10253                 e.ctrlKey ?
10254                     this.showNextMonth() :
10255                     this.update(this.activeDate.add("d", 1));
10256             },
10257
10258             "up" : function(e){
10259                 e.ctrlKey ?
10260                     this.showNextYear() :
10261                     this.update(this.activeDate.add("d", -7));
10262             },
10263
10264             "down" : function(e){
10265                 e.ctrlKey ?
10266                     this.showPrevYear() :
10267                     this.update(this.activeDate.add("d", 7));
10268             },
10269
10270             "pageUp" : function(e){
10271                 this.showNextMonth();
10272             },
10273
10274             "pageDown" : function(e){
10275                 this.showPrevMonth();
10276             },
10277
10278             "enter" : function(e){
10279                 e.stopPropagation();
10280                 return true;
10281             },
10282
10283             scope : this
10284         });
10285
10286         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10287
10288         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10289
10290         this.el.unselectable();
10291         
10292         this.cells = this.el.select("table.x-date-inner tbody td");
10293         this.textNodes = this.el.query("table.x-date-inner tbody span");
10294
10295         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10296             text: "&#160;",
10297             tooltip: this.monthYearText
10298         });
10299
10300         this.mbtn.on('click', this.showMonthPicker, this);
10301         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10302
10303
10304         var today = (new Date()).dateFormat(this.format);
10305         
10306         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10307         if (this.showClear) {
10308             baseTb.add( new Roo.Toolbar.Fill());
10309         }
10310         baseTb.add({
10311             text: String.format(this.todayText, today),
10312             tooltip: String.format(this.todayTip, today),
10313             handler: this.selectToday,
10314             scope: this
10315         });
10316         
10317         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10318             
10319         //});
10320         if (this.showClear) {
10321             
10322             baseTb.add( new Roo.Toolbar.Fill());
10323             baseTb.add({
10324                 text: '&#160;',
10325                 cls: 'x-btn-icon x-btn-clear',
10326                 handler: function() {
10327                     //this.value = '';
10328                     this.fireEvent("select", this, '');
10329                 },
10330                 scope: this
10331             });
10332         }
10333         
10334         
10335         if(Roo.isIE){
10336             this.el.repaint();
10337         }
10338         this.update(this.value);
10339     },
10340
10341     createMonthPicker : function(){
10342         if(!this.monthPicker.dom.firstChild){
10343             var buf = ['<table border="0" cellspacing="0">'];
10344             for(var i = 0; i < 6; i++){
10345                 buf.push(
10346                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10347                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10348                     i == 0 ?
10349                     '<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>' :
10350                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10351                 );
10352             }
10353             buf.push(
10354                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10355                     this.okText,
10356                     '</button><button type="button" class="x-date-mp-cancel">',
10357                     this.cancelText,
10358                     '</button></td></tr>',
10359                 '</table>'
10360             );
10361             this.monthPicker.update(buf.join(''));
10362             this.monthPicker.on('click', this.onMonthClick, this);
10363             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10364
10365             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10366             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10367
10368             this.mpMonths.each(function(m, a, i){
10369                 i += 1;
10370                 if((i%2) == 0){
10371                     m.dom.xmonth = 5 + Math.round(i * .5);
10372                 }else{
10373                     m.dom.xmonth = Math.round((i-1) * .5);
10374                 }
10375             });
10376         }
10377     },
10378
10379     showMonthPicker : function(){
10380         this.createMonthPicker();
10381         var size = this.el.getSize();
10382         this.monthPicker.setSize(size);
10383         this.monthPicker.child('table').setSize(size);
10384
10385         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10386         this.updateMPMonth(this.mpSelMonth);
10387         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10388         this.updateMPYear(this.mpSelYear);
10389
10390         this.monthPicker.slideIn('t', {duration:.2});
10391     },
10392
10393     updateMPYear : function(y){
10394         this.mpyear = y;
10395         var ys = this.mpYears.elements;
10396         for(var i = 1; i <= 10; i++){
10397             var td = ys[i-1], y2;
10398             if((i%2) == 0){
10399                 y2 = y + Math.round(i * .5);
10400                 td.firstChild.innerHTML = y2;
10401                 td.xyear = y2;
10402             }else{
10403                 y2 = y - (5-Math.round(i * .5));
10404                 td.firstChild.innerHTML = y2;
10405                 td.xyear = y2;
10406             }
10407             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10408         }
10409     },
10410
10411     updateMPMonth : function(sm){
10412         this.mpMonths.each(function(m, a, i){
10413             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10414         });
10415     },
10416
10417     selectMPMonth: function(m){
10418         
10419     },
10420
10421     onMonthClick : function(e, t){
10422         e.stopEvent();
10423         var el = new Roo.Element(t), pn;
10424         if(el.is('button.x-date-mp-cancel')){
10425             this.hideMonthPicker();
10426         }
10427         else if(el.is('button.x-date-mp-ok')){
10428             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10429             this.hideMonthPicker();
10430         }
10431         else if(pn = el.up('td.x-date-mp-month', 2)){
10432             this.mpMonths.removeClass('x-date-mp-sel');
10433             pn.addClass('x-date-mp-sel');
10434             this.mpSelMonth = pn.dom.xmonth;
10435         }
10436         else if(pn = el.up('td.x-date-mp-year', 2)){
10437             this.mpYears.removeClass('x-date-mp-sel');
10438             pn.addClass('x-date-mp-sel');
10439             this.mpSelYear = pn.dom.xyear;
10440         }
10441         else if(el.is('a.x-date-mp-prev')){
10442             this.updateMPYear(this.mpyear-10);
10443         }
10444         else if(el.is('a.x-date-mp-next')){
10445             this.updateMPYear(this.mpyear+10);
10446         }
10447     },
10448
10449     onMonthDblClick : function(e, t){
10450         e.stopEvent();
10451         var el = new Roo.Element(t), pn;
10452         if(pn = el.up('td.x-date-mp-month', 2)){
10453             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10454             this.hideMonthPicker();
10455         }
10456         else if(pn = el.up('td.x-date-mp-year', 2)){
10457             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10458             this.hideMonthPicker();
10459         }
10460     },
10461
10462     hideMonthPicker : function(disableAnim){
10463         if(this.monthPicker){
10464             if(disableAnim === true){
10465                 this.monthPicker.hide();
10466             }else{
10467                 this.monthPicker.slideOut('t', {duration:.2});
10468             }
10469         }
10470     },
10471
10472     // private
10473     showPrevMonth : function(e){
10474         this.update(this.activeDate.add("mo", -1));
10475     },
10476
10477     // private
10478     showNextMonth : function(e){
10479         this.update(this.activeDate.add("mo", 1));
10480     },
10481
10482     // private
10483     showPrevYear : function(){
10484         this.update(this.activeDate.add("y", -1));
10485     },
10486
10487     // private
10488     showNextYear : function(){
10489         this.update(this.activeDate.add("y", 1));
10490     },
10491
10492     // private
10493     handleMouseWheel : function(e){
10494         var delta = e.getWheelDelta();
10495         if(delta > 0){
10496             this.showPrevMonth();
10497             e.stopEvent();
10498         } else if(delta < 0){
10499             this.showNextMonth();
10500             e.stopEvent();
10501         }
10502     },
10503
10504     // private
10505     handleDateClick : function(e, t){
10506         e.stopEvent();
10507         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10508             this.setValue(new Date(t.dateValue));
10509             this.fireEvent("select", this, this.value);
10510         }
10511     },
10512
10513     // private
10514     selectToday : function(){
10515         this.setValue(new Date().clearTime());
10516         this.fireEvent("select", this, this.value);
10517     },
10518
10519     // private
10520     update : function(date){
10521         var vd = this.activeDate;
10522         this.activeDate = date;
10523         if(vd && this.el){
10524             var t = date.getTime();
10525             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10526                 this.cells.removeClass("x-date-selected");
10527                 this.cells.each(function(c){
10528                    if(c.dom.firstChild.dateValue == t){
10529                        c.addClass("x-date-selected");
10530                        setTimeout(function(){
10531                             try{c.dom.firstChild.focus();}catch(e){}
10532                        }, 50);
10533                        return false;
10534                    }
10535                 });
10536                 return;
10537             }
10538         }
10539         var days = date.getDaysInMonth();
10540         var firstOfMonth = date.getFirstDateOfMonth();
10541         var startingPos = firstOfMonth.getDay()-this.startDay;
10542
10543         if(startingPos <= this.startDay){
10544             startingPos += 7;
10545         }
10546
10547         var pm = date.add("mo", -1);
10548         var prevStart = pm.getDaysInMonth()-startingPos;
10549
10550         var cells = this.cells.elements;
10551         var textEls = this.textNodes;
10552         days += startingPos;
10553
10554         // convert everything to numbers so it's fast
10555         var day = 86400000;
10556         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10557         var today = new Date().clearTime().getTime();
10558         var sel = date.clearTime().getTime();
10559         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10560         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10561         var ddMatch = this.disabledDatesRE;
10562         var ddText = this.disabledDatesText;
10563         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10564         var ddaysText = this.disabledDaysText;
10565         var format = this.format;
10566
10567         var setCellClass = function(cal, cell){
10568             cell.title = "";
10569             var t = d.getTime();
10570             cell.firstChild.dateValue = t;
10571             if(t == today){
10572                 cell.className += " x-date-today";
10573                 cell.title = cal.todayText;
10574             }
10575             if(t == sel){
10576                 cell.className += " x-date-selected";
10577                 setTimeout(function(){
10578                     try{cell.firstChild.focus();}catch(e){}
10579                 }, 50);
10580             }
10581             // disabling
10582             if(t < min) {
10583                 cell.className = " x-date-disabled";
10584                 cell.title = cal.minText;
10585                 return;
10586             }
10587             if(t > max) {
10588                 cell.className = " x-date-disabled";
10589                 cell.title = cal.maxText;
10590                 return;
10591             }
10592             if(ddays){
10593                 if(ddays.indexOf(d.getDay()) != -1){
10594                     cell.title = ddaysText;
10595                     cell.className = " x-date-disabled";
10596                 }
10597             }
10598             if(ddMatch && format){
10599                 var fvalue = d.dateFormat(format);
10600                 if(ddMatch.test(fvalue)){
10601                     cell.title = ddText.replace("%0", fvalue);
10602                     cell.className = " x-date-disabled";
10603                 }
10604             }
10605         };
10606
10607         var i = 0;
10608         for(; i < startingPos; i++) {
10609             textEls[i].innerHTML = (++prevStart);
10610             d.setDate(d.getDate()+1);
10611             cells[i].className = "x-date-prevday";
10612             setCellClass(this, cells[i]);
10613         }
10614         for(; i < days; i++){
10615             intDay = i - startingPos + 1;
10616             textEls[i].innerHTML = (intDay);
10617             d.setDate(d.getDate()+1);
10618             cells[i].className = "x-date-active";
10619             setCellClass(this, cells[i]);
10620         }
10621         var extraDays = 0;
10622         for(; i < 42; i++) {
10623              textEls[i].innerHTML = (++extraDays);
10624              d.setDate(d.getDate()+1);
10625              cells[i].className = "x-date-nextday";
10626              setCellClass(this, cells[i]);
10627         }
10628
10629         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10630
10631         if(!this.internalRender){
10632             var main = this.el.dom.firstChild;
10633             var w = main.offsetWidth;
10634             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10635             Roo.fly(main).setWidth(w);
10636             this.internalRender = true;
10637             // opera does not respect the auto grow header center column
10638             // then, after it gets a width opera refuses to recalculate
10639             // without a second pass
10640             if(Roo.isOpera && !this.secondPass){
10641                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10642                 this.secondPass = true;
10643                 this.update.defer(10, this, [date]);
10644             }
10645         }
10646     }
10647 });/*
10648  * Based on:
10649  * Ext JS Library 1.1.1
10650  * Copyright(c) 2006-2007, Ext JS, LLC.
10651  *
10652  * Originally Released Under LGPL - original licence link has changed is not relivant.
10653  *
10654  * Fork - LGPL
10655  * <script type="text/javascript">
10656  */
10657 /**
10658  * @class Roo.TabPanel
10659  * @extends Roo.util.Observable
10660  * A lightweight tab container.
10661  * <br><br>
10662  * Usage:
10663  * <pre><code>
10664 // basic tabs 1, built from existing content
10665 var tabs = new Roo.TabPanel("tabs1");
10666 tabs.addTab("script", "View Script");
10667 tabs.addTab("markup", "View Markup");
10668 tabs.activate("script");
10669
10670 // more advanced tabs, built from javascript
10671 var jtabs = new Roo.TabPanel("jtabs");
10672 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10673
10674 // set up the UpdateManager
10675 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10676 var updater = tab2.getUpdateManager();
10677 updater.setDefaultUrl("ajax1.htm");
10678 tab2.on('activate', updater.refresh, updater, true);
10679
10680 // Use setUrl for Ajax loading
10681 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10682 tab3.setUrl("ajax2.htm", null, true);
10683
10684 // Disabled tab
10685 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10686 tab4.disable();
10687
10688 jtabs.activate("jtabs-1");
10689  * </code></pre>
10690  * @constructor
10691  * Create a new TabPanel.
10692  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10693  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10694  */
10695 Roo.TabPanel = function(container, config){
10696     /**
10697     * The container element for this TabPanel.
10698     * @type Roo.Element
10699     */
10700     this.el = Roo.get(container, true);
10701     if(config){
10702         if(typeof config == "boolean"){
10703             this.tabPosition = config ? "bottom" : "top";
10704         }else{
10705             Roo.apply(this, config);
10706         }
10707     }
10708     if(this.tabPosition == "bottom"){
10709         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10710         this.el.addClass("x-tabs-bottom");
10711     }
10712     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10713     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10714     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10715     if(Roo.isIE){
10716         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10717     }
10718     if(this.tabPosition != "bottom"){
10719     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10720      * @type Roo.Element
10721      */
10722       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10723       this.el.addClass("x-tabs-top");
10724     }
10725     this.items = [];
10726
10727     this.bodyEl.setStyle("position", "relative");
10728
10729     this.active = null;
10730     this.activateDelegate = this.activate.createDelegate(this);
10731
10732     this.addEvents({
10733         /**
10734          * @event tabchange
10735          * Fires when the active tab changes
10736          * @param {Roo.TabPanel} this
10737          * @param {Roo.TabPanelItem} activePanel The new active tab
10738          */
10739         "tabchange": true,
10740         /**
10741          * @event beforetabchange
10742          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10743          * @param {Roo.TabPanel} this
10744          * @param {Object} e Set cancel to true on this object to cancel the tab change
10745          * @param {Roo.TabPanelItem} tab The tab being changed to
10746          */
10747         "beforetabchange" : true
10748     });
10749
10750     Roo.EventManager.onWindowResize(this.onResize, this);
10751     this.cpad = this.el.getPadding("lr");
10752     this.hiddenCount = 0;
10753
10754     Roo.TabPanel.superclass.constructor.call(this);
10755 };
10756
10757 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10758         /*
10759          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10760          */
10761     tabPosition : "top",
10762         /*
10763          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10764          */
10765     currentTabWidth : 0,
10766         /*
10767          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10768          */
10769     minTabWidth : 40,
10770         /*
10771          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10772          */
10773     maxTabWidth : 250,
10774         /*
10775          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10776          */
10777     preferredTabWidth : 175,
10778         /*
10779          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10780          */
10781     resizeTabs : false,
10782         /*
10783          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10784          */
10785     monitorResize : true,
10786
10787     /**
10788      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10789      * @param {String} id The id of the div to use <b>or create</b>
10790      * @param {String} text The text for the tab
10791      * @param {String} content (optional) Content to put in the TabPanelItem body
10792      * @param {Boolean} closable (optional) True to create a close icon on the tab
10793      * @return {Roo.TabPanelItem} The created TabPanelItem
10794      */
10795     addTab : function(id, text, content, closable){
10796         var item = new Roo.TabPanelItem(this, id, text, closable);
10797         this.addTabItem(item);
10798         if(content){
10799             item.setContent(content);
10800         }
10801         return item;
10802     },
10803
10804     /**
10805      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10806      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10807      * @return {Roo.TabPanelItem}
10808      */
10809     getTab : function(id){
10810         return this.items[id];
10811     },
10812
10813     /**
10814      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10815      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10816      */
10817     hideTab : function(id){
10818         var t = this.items[id];
10819         if(!t.isHidden()){
10820            t.setHidden(true);
10821            this.hiddenCount++;
10822            this.autoSizeTabs();
10823         }
10824     },
10825
10826     /**
10827      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10828      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10829      */
10830     unhideTab : function(id){
10831         var t = this.items[id];
10832         if(t.isHidden()){
10833            t.setHidden(false);
10834            this.hiddenCount--;
10835            this.autoSizeTabs();
10836         }
10837     },
10838
10839     /**
10840      * Adds an existing {@link Roo.TabPanelItem}.
10841      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10842      */
10843     addTabItem : function(item){
10844         this.items[item.id] = item;
10845         this.items.push(item);
10846         if(this.resizeTabs){
10847            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10848            this.autoSizeTabs();
10849         }else{
10850             item.autoSize();
10851         }
10852     },
10853
10854     /**
10855      * Removes a {@link Roo.TabPanelItem}.
10856      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10857      */
10858     removeTab : function(id){
10859         var items = this.items;
10860         var tab = items[id];
10861         if(!tab) { return; }
10862         var index = items.indexOf(tab);
10863         if(this.active == tab && items.length > 1){
10864             var newTab = this.getNextAvailable(index);
10865             if(newTab) {
10866                 newTab.activate();
10867             }
10868         }
10869         this.stripEl.dom.removeChild(tab.pnode.dom);
10870         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10871             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10872         }
10873         items.splice(index, 1);
10874         delete this.items[tab.id];
10875         tab.fireEvent("close", tab);
10876         tab.purgeListeners();
10877         this.autoSizeTabs();
10878     },
10879
10880     getNextAvailable : function(start){
10881         var items = this.items;
10882         var index = start;
10883         // look for a next tab that will slide over to
10884         // replace the one being removed
10885         while(index < items.length){
10886             var item = items[++index];
10887             if(item && !item.isHidden()){
10888                 return item;
10889             }
10890         }
10891         // if one isn't found select the previous tab (on the left)
10892         index = start;
10893         while(index >= 0){
10894             var item = items[--index];
10895             if(item && !item.isHidden()){
10896                 return item;
10897             }
10898         }
10899         return null;
10900     },
10901
10902     /**
10903      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10904      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10905      */
10906     disableTab : function(id){
10907         var tab = this.items[id];
10908         if(tab && this.active != tab){
10909             tab.disable();
10910         }
10911     },
10912
10913     /**
10914      * Enables a {@link Roo.TabPanelItem} that is disabled.
10915      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10916      */
10917     enableTab : function(id){
10918         var tab = this.items[id];
10919         tab.enable();
10920     },
10921
10922     /**
10923      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10924      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10925      * @return {Roo.TabPanelItem} The TabPanelItem.
10926      */
10927     activate : function(id){
10928         var tab = this.items[id];
10929         if(!tab){
10930             return null;
10931         }
10932         if(tab == this.active || tab.disabled){
10933             return tab;
10934         }
10935         var e = {};
10936         this.fireEvent("beforetabchange", this, e, tab);
10937         if(e.cancel !== true && !tab.disabled){
10938             if(this.active){
10939                 this.active.hide();
10940             }
10941             this.active = this.items[id];
10942             this.active.show();
10943             this.fireEvent("tabchange", this, this.active);
10944         }
10945         return tab;
10946     },
10947
10948     /**
10949      * Gets the active {@link Roo.TabPanelItem}.
10950      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10951      */
10952     getActiveTab : function(){
10953         return this.active;
10954     },
10955
10956     /**
10957      * Updates the tab body element to fit the height of the container element
10958      * for overflow scrolling
10959      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10960      */
10961     syncHeight : function(targetHeight){
10962         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10963         var bm = this.bodyEl.getMargins();
10964         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10965         this.bodyEl.setHeight(newHeight);
10966         return newHeight;
10967     },
10968
10969     onResize : function(){
10970         if(this.monitorResize){
10971             this.autoSizeTabs();
10972         }
10973     },
10974
10975     /**
10976      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10977      */
10978     beginUpdate : function(){
10979         this.updating = true;
10980     },
10981
10982     /**
10983      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10984      */
10985     endUpdate : function(){
10986         this.updating = false;
10987         this.autoSizeTabs();
10988     },
10989
10990     /**
10991      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10992      */
10993     autoSizeTabs : function(){
10994         var count = this.items.length;
10995         var vcount = count - this.hiddenCount;
10996         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10997         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10998         var availWidth = Math.floor(w / vcount);
10999         var b = this.stripBody;
11000         if(b.getWidth() > w){
11001             var tabs = this.items;
11002             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11003             if(availWidth < this.minTabWidth){
11004                 /*if(!this.sleft){    // incomplete scrolling code
11005                     this.createScrollButtons();
11006                 }
11007                 this.showScroll();
11008                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11009             }
11010         }else{
11011             if(this.currentTabWidth < this.preferredTabWidth){
11012                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11013             }
11014         }
11015     },
11016
11017     /**
11018      * Returns the number of tabs in this TabPanel.
11019      * @return {Number}
11020      */
11021      getCount : function(){
11022          return this.items.length;
11023      },
11024
11025     /**
11026      * Resizes all the tabs to the passed width
11027      * @param {Number} The new width
11028      */
11029     setTabWidth : function(width){
11030         this.currentTabWidth = width;
11031         for(var i = 0, len = this.items.length; i < len; i++) {
11032                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11033         }
11034     },
11035
11036     /**
11037      * Destroys this TabPanel
11038      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11039      */
11040     destroy : function(removeEl){
11041         Roo.EventManager.removeResizeListener(this.onResize, this);
11042         for(var i = 0, len = this.items.length; i < len; i++){
11043             this.items[i].purgeListeners();
11044         }
11045         if(removeEl === true){
11046             this.el.update("");
11047             this.el.remove();
11048         }
11049     }
11050 });
11051
11052 /**
11053  * @class Roo.TabPanelItem
11054  * @extends Roo.util.Observable
11055  * Represents an individual item (tab plus body) in a TabPanel.
11056  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11057  * @param {String} id The id of this TabPanelItem
11058  * @param {String} text The text for the tab of this TabPanelItem
11059  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11060  */
11061 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11062     /**
11063      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11064      * @type Roo.TabPanel
11065      */
11066     this.tabPanel = tabPanel;
11067     /**
11068      * The id for this TabPanelItem
11069      * @type String
11070      */
11071     this.id = id;
11072     /** @private */
11073     this.disabled = false;
11074     /** @private */
11075     this.text = text;
11076     /** @private */
11077     this.loaded = false;
11078     this.closable = closable;
11079
11080     /**
11081      * The body element for this TabPanelItem.
11082      * @type Roo.Element
11083      */
11084     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11085     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11086     this.bodyEl.setStyle("display", "block");
11087     this.bodyEl.setStyle("zoom", "1");
11088     this.hideAction();
11089
11090     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11091     /** @private */
11092     this.el = Roo.get(els.el, true);
11093     this.inner = Roo.get(els.inner, true);
11094     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11095     this.pnode = Roo.get(els.el.parentNode, true);
11096     this.el.on("mousedown", this.onTabMouseDown, this);
11097     this.el.on("click", this.onTabClick, this);
11098     /** @private */
11099     if(closable){
11100         var c = Roo.get(els.close, true);
11101         c.dom.title = this.closeText;
11102         c.addClassOnOver("close-over");
11103         c.on("click", this.closeClick, this);
11104      }
11105
11106     this.addEvents({
11107          /**
11108          * @event activate
11109          * Fires when this tab becomes the active tab.
11110          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11111          * @param {Roo.TabPanelItem} this
11112          */
11113         "activate": true,
11114         /**
11115          * @event beforeclose
11116          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11117          * @param {Roo.TabPanelItem} this
11118          * @param {Object} e Set cancel to true on this object to cancel the close.
11119          */
11120         "beforeclose": true,
11121         /**
11122          * @event close
11123          * Fires when this tab is closed.
11124          * @param {Roo.TabPanelItem} this
11125          */
11126          "close": true,
11127         /**
11128          * @event deactivate
11129          * Fires when this tab is no longer the active tab.
11130          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11131          * @param {Roo.TabPanelItem} this
11132          */
11133          "deactivate" : true
11134     });
11135     this.hidden = false;
11136
11137     Roo.TabPanelItem.superclass.constructor.call(this);
11138 };
11139
11140 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11141     purgeListeners : function(){
11142        Roo.util.Observable.prototype.purgeListeners.call(this);
11143        this.el.removeAllListeners();
11144     },
11145     /**
11146      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11147      */
11148     show : function(){
11149         this.pnode.addClass("on");
11150         this.showAction();
11151         if(Roo.isOpera){
11152             this.tabPanel.stripWrap.repaint();
11153         }
11154         this.fireEvent("activate", this.tabPanel, this);
11155     },
11156
11157     /**
11158      * Returns true if this tab is the active tab.
11159      * @return {Boolean}
11160      */
11161     isActive : function(){
11162         return this.tabPanel.getActiveTab() == this;
11163     },
11164
11165     /**
11166      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11167      */
11168     hide : function(){
11169         this.pnode.removeClass("on");
11170         this.hideAction();
11171         this.fireEvent("deactivate", this.tabPanel, this);
11172     },
11173
11174     hideAction : function(){
11175         this.bodyEl.hide();
11176         this.bodyEl.setStyle("position", "absolute");
11177         this.bodyEl.setLeft("-20000px");
11178         this.bodyEl.setTop("-20000px");
11179     },
11180
11181     showAction : function(){
11182         this.bodyEl.setStyle("position", "relative");
11183         this.bodyEl.setTop("");
11184         this.bodyEl.setLeft("");
11185         this.bodyEl.show();
11186     },
11187
11188     /**
11189      * Set the tooltip for the tab.
11190      * @param {String} tooltip The tab's tooltip
11191      */
11192     setTooltip : function(text){
11193         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11194             this.textEl.dom.qtip = text;
11195             this.textEl.dom.removeAttribute('title');
11196         }else{
11197             this.textEl.dom.title = text;
11198         }
11199     },
11200
11201     onTabClick : function(e){
11202         e.preventDefault();
11203         this.tabPanel.activate(this.id);
11204     },
11205
11206     onTabMouseDown : function(e){
11207         e.preventDefault();
11208         this.tabPanel.activate(this.id);
11209     },
11210
11211     getWidth : function(){
11212         return this.inner.getWidth();
11213     },
11214
11215     setWidth : function(width){
11216         var iwidth = width - this.pnode.getPadding("lr");
11217         this.inner.setWidth(iwidth);
11218         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11219         this.pnode.setWidth(width);
11220     },
11221
11222     /**
11223      * Show or hide the tab
11224      * @param {Boolean} hidden True to hide or false to show.
11225      */
11226     setHidden : function(hidden){
11227         this.hidden = hidden;
11228         this.pnode.setStyle("display", hidden ? "none" : "");
11229     },
11230
11231     /**
11232      * Returns true if this tab is "hidden"
11233      * @return {Boolean}
11234      */
11235     isHidden : function(){
11236         return this.hidden;
11237     },
11238
11239     /**
11240      * Returns the text for this tab
11241      * @return {String}
11242      */
11243     getText : function(){
11244         return this.text;
11245     },
11246
11247     autoSize : function(){
11248         //this.el.beginMeasure();
11249         this.textEl.setWidth(1);
11250         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11251         //this.el.endMeasure();
11252     },
11253
11254     /**
11255      * Sets the text for the tab (Note: this also sets the tooltip text)
11256      * @param {String} text The tab's text and tooltip
11257      */
11258     setText : function(text){
11259         this.text = text;
11260         this.textEl.update(text);
11261         this.setTooltip(text);
11262         if(!this.tabPanel.resizeTabs){
11263             this.autoSize();
11264         }
11265     },
11266     /**
11267      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11268      */
11269     activate : function(){
11270         this.tabPanel.activate(this.id);
11271     },
11272
11273     /**
11274      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11275      */
11276     disable : function(){
11277         if(this.tabPanel.active != this){
11278             this.disabled = true;
11279             this.pnode.addClass("disabled");
11280         }
11281     },
11282
11283     /**
11284      * Enables this TabPanelItem if it was previously disabled.
11285      */
11286     enable : function(){
11287         this.disabled = false;
11288         this.pnode.removeClass("disabled");
11289     },
11290
11291     /**
11292      * Sets the content for this TabPanelItem.
11293      * @param {String} content The content
11294      * @param {Boolean} loadScripts true to look for and load scripts
11295      */
11296     setContent : function(content, loadScripts){
11297         this.bodyEl.update(content, loadScripts);
11298     },
11299
11300     /**
11301      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11302      * @return {Roo.UpdateManager} The UpdateManager
11303      */
11304     getUpdateManager : function(){
11305         return this.bodyEl.getUpdateManager();
11306     },
11307
11308     /**
11309      * Set a URL to be used to load the content for this TabPanelItem.
11310      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11311      * @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)
11312      * @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)
11313      * @return {Roo.UpdateManager} The UpdateManager
11314      */
11315     setUrl : function(url, params, loadOnce){
11316         if(this.refreshDelegate){
11317             this.un('activate', this.refreshDelegate);
11318         }
11319         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11320         this.on("activate", this.refreshDelegate);
11321         return this.bodyEl.getUpdateManager();
11322     },
11323
11324     /** @private */
11325     _handleRefresh : function(url, params, loadOnce){
11326         if(!loadOnce || !this.loaded){
11327             var updater = this.bodyEl.getUpdateManager();
11328             updater.update(url, params, this._setLoaded.createDelegate(this));
11329         }
11330     },
11331
11332     /**
11333      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11334      *   Will fail silently if the setUrl method has not been called.
11335      *   This does not activate the panel, just updates its content.
11336      */
11337     refresh : function(){
11338         if(this.refreshDelegate){
11339            this.loaded = false;
11340            this.refreshDelegate();
11341         }
11342     },
11343
11344     /** @private */
11345     _setLoaded : function(){
11346         this.loaded = true;
11347     },
11348
11349     /** @private */
11350     closeClick : function(e){
11351         var o = {};
11352         e.stopEvent();
11353         this.fireEvent("beforeclose", this, o);
11354         if(o.cancel !== true){
11355             this.tabPanel.removeTab(this.id);
11356         }
11357     },
11358     /**
11359      * The text displayed in the tooltip for the close icon.
11360      * @type String
11361      */
11362     closeText : "Close this tab"
11363 });
11364
11365 /** @private */
11366 Roo.TabPanel.prototype.createStrip = function(container){
11367     var strip = document.createElement("div");
11368     strip.className = "x-tabs-wrap";
11369     container.appendChild(strip);
11370     return strip;
11371 };
11372 /** @private */
11373 Roo.TabPanel.prototype.createStripList = function(strip){
11374     // div wrapper for retard IE
11375     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11376     return strip.firstChild.firstChild.firstChild.firstChild;
11377 };
11378 /** @private */
11379 Roo.TabPanel.prototype.createBody = function(container){
11380     var body = document.createElement("div");
11381     Roo.id(body, "tab-body");
11382     Roo.fly(body).addClass("x-tabs-body");
11383     container.appendChild(body);
11384     return body;
11385 };
11386 /** @private */
11387 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11388     var body = Roo.getDom(id);
11389     if(!body){
11390         body = document.createElement("div");
11391         body.id = id;
11392     }
11393     Roo.fly(body).addClass("x-tabs-item-body");
11394     bodyEl.insertBefore(body, bodyEl.firstChild);
11395     return body;
11396 };
11397 /** @private */
11398 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11399     var td = document.createElement("td");
11400     stripEl.appendChild(td);
11401     if(closable){
11402         td.className = "x-tabs-closable";
11403         if(!this.closeTpl){
11404             this.closeTpl = new Roo.Template(
11405                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11406                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11407                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11408             );
11409         }
11410         var el = this.closeTpl.overwrite(td, {"text": text});
11411         var close = el.getElementsByTagName("div")[0];
11412         var inner = el.getElementsByTagName("em")[0];
11413         return {"el": el, "close": close, "inner": inner};
11414     } else {
11415         if(!this.tabTpl){
11416             this.tabTpl = new Roo.Template(
11417                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11418                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11419             );
11420         }
11421         var el = this.tabTpl.overwrite(td, {"text": text});
11422         var inner = el.getElementsByTagName("em")[0];
11423         return {"el": el, "inner": inner};
11424     }
11425 };/*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435
11436 /**
11437  * @class Roo.Button
11438  * @extends Roo.util.Observable
11439  * Simple Button class
11440  * @cfg {String} text The button text
11441  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11442  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11443  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11444  * @cfg {Object} scope The scope of the handler
11445  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11446  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11447  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11448  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11449  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11450  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11451    applies if enableToggle = true)
11452  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11453  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11454   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11455  * @constructor
11456  * Create a new button
11457  * @param {Object} config The config object
11458  */
11459 Roo.Button = function(renderTo, config)
11460 {
11461     if (!config) {
11462         config = renderTo;
11463         renderTo = config.renderTo || false;
11464     }
11465     
11466     Roo.apply(this, config);
11467     this.addEvents({
11468         /**
11469              * @event click
11470              * Fires when this button is clicked
11471              * @param {Button} this
11472              * @param {EventObject} e The click event
11473              */
11474             "click" : true,
11475         /**
11476              * @event toggle
11477              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11478              * @param {Button} this
11479              * @param {Boolean} pressed
11480              */
11481             "toggle" : true,
11482         /**
11483              * @event mouseover
11484              * Fires when the mouse hovers over the button
11485              * @param {Button} this
11486              * @param {Event} e The event object
11487              */
11488         'mouseover' : true,
11489         /**
11490              * @event mouseout
11491              * Fires when the mouse exits the button
11492              * @param {Button} this
11493              * @param {Event} e The event object
11494              */
11495         'mouseout': true,
11496          /**
11497              * @event render
11498              * Fires when the button is rendered
11499              * @param {Button} this
11500              */
11501         'render': true
11502     });
11503     if(this.menu){
11504         this.menu = Roo.menu.MenuMgr.get(this.menu);
11505     }
11506     // register listeners first!!  - so render can be captured..
11507     Roo.util.Observable.call(this);
11508     if(renderTo){
11509         this.render(renderTo);
11510     }
11511     
11512   
11513 };
11514
11515 Roo.extend(Roo.Button, Roo.util.Observable, {
11516     /**
11517      * 
11518      */
11519     
11520     /**
11521      * Read-only. True if this button is hidden
11522      * @type Boolean
11523      */
11524     hidden : false,
11525     /**
11526      * Read-only. True if this button is disabled
11527      * @type Boolean
11528      */
11529     disabled : false,
11530     /**
11531      * Read-only. True if this button is pressed (only if enableToggle = true)
11532      * @type Boolean
11533      */
11534     pressed : false,
11535
11536     /**
11537      * @cfg {Number} tabIndex 
11538      * The DOM tabIndex for this button (defaults to undefined)
11539      */
11540     tabIndex : undefined,
11541
11542     /**
11543      * @cfg {Boolean} enableToggle
11544      * True to enable pressed/not pressed toggling (defaults to false)
11545      */
11546     enableToggle: false,
11547     /**
11548      * @cfg {Mixed} menu
11549      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11550      */
11551     menu : undefined,
11552     /**
11553      * @cfg {String} menuAlign
11554      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11555      */
11556     menuAlign : "tl-bl?",
11557
11558     /**
11559      * @cfg {String} iconCls
11560      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11561      */
11562     iconCls : undefined,
11563     /**
11564      * @cfg {String} type
11565      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11566      */
11567     type : 'button',
11568
11569     // private
11570     menuClassTarget: 'tr',
11571
11572     /**
11573      * @cfg {String} clickEvent
11574      * The type of event to map to the button's event handler (defaults to 'click')
11575      */
11576     clickEvent : 'click',
11577
11578     /**
11579      * @cfg {Boolean} handleMouseEvents
11580      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11581      */
11582     handleMouseEvents : true,
11583
11584     /**
11585      * @cfg {String} tooltipType
11586      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11587      */
11588     tooltipType : 'qtip',
11589
11590     /**
11591      * @cfg {String} cls
11592      * A CSS class to apply to the button's main element.
11593      */
11594     
11595     /**
11596      * @cfg {Roo.Template} template (Optional)
11597      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11598      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11599      * require code modifications if required elements (e.g. a button) aren't present.
11600      */
11601
11602     // private
11603     render : function(renderTo){
11604         var btn;
11605         if(this.hideParent){
11606             this.parentEl = Roo.get(renderTo);
11607         }
11608         if(!this.dhconfig){
11609             if(!this.template){
11610                 if(!Roo.Button.buttonTemplate){
11611                     // hideous table template
11612                     Roo.Button.buttonTemplate = new Roo.Template(
11613                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11614                         '<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>',
11615                         "</tr></tbody></table>");
11616                 }
11617                 this.template = Roo.Button.buttonTemplate;
11618             }
11619             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11620             var btnEl = btn.child("button:first");
11621             btnEl.on('focus', this.onFocus, this);
11622             btnEl.on('blur', this.onBlur, this);
11623             if(this.cls){
11624                 btn.addClass(this.cls);
11625             }
11626             if(this.icon){
11627                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11628             }
11629             if(this.iconCls){
11630                 btnEl.addClass(this.iconCls);
11631                 if(!this.cls){
11632                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11633                 }
11634             }
11635             if(this.tabIndex !== undefined){
11636                 btnEl.dom.tabIndex = this.tabIndex;
11637             }
11638             if(this.tooltip){
11639                 if(typeof this.tooltip == 'object'){
11640                     Roo.QuickTips.tips(Roo.apply({
11641                           target: btnEl.id
11642                     }, this.tooltip));
11643                 } else {
11644                     btnEl.dom[this.tooltipType] = this.tooltip;
11645                 }
11646             }
11647         }else{
11648             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11649         }
11650         this.el = btn;
11651         if(this.id){
11652             this.el.dom.id = this.el.id = this.id;
11653         }
11654         if(this.menu){
11655             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11656             this.menu.on("show", this.onMenuShow, this);
11657             this.menu.on("hide", this.onMenuHide, this);
11658         }
11659         btn.addClass("x-btn");
11660         if(Roo.isIE && !Roo.isIE7){
11661             this.autoWidth.defer(1, this);
11662         }else{
11663             this.autoWidth();
11664         }
11665         if(this.handleMouseEvents){
11666             btn.on("mouseover", this.onMouseOver, this);
11667             btn.on("mouseout", this.onMouseOut, this);
11668             btn.on("mousedown", this.onMouseDown, this);
11669         }
11670         btn.on(this.clickEvent, this.onClick, this);
11671         //btn.on("mouseup", this.onMouseUp, this);
11672         if(this.hidden){
11673             this.hide();
11674         }
11675         if(this.disabled){
11676             this.disable();
11677         }
11678         Roo.ButtonToggleMgr.register(this);
11679         if(this.pressed){
11680             this.el.addClass("x-btn-pressed");
11681         }
11682         if(this.repeat){
11683             var repeater = new Roo.util.ClickRepeater(btn,
11684                 typeof this.repeat == "object" ? this.repeat : {}
11685             );
11686             repeater.on("click", this.onClick,  this);
11687         }
11688         
11689         this.fireEvent('render', this);
11690         
11691     },
11692     /**
11693      * Returns the button's underlying element
11694      * @return {Roo.Element} The element
11695      */
11696     getEl : function(){
11697         return this.el;  
11698     },
11699     
11700     /**
11701      * Destroys this Button and removes any listeners.
11702      */
11703     destroy : function(){
11704         Roo.ButtonToggleMgr.unregister(this);
11705         this.el.removeAllListeners();
11706         this.purgeListeners();
11707         this.el.remove();
11708     },
11709
11710     // private
11711     autoWidth : function(){
11712         if(this.el){
11713             this.el.setWidth("auto");
11714             if(Roo.isIE7 && Roo.isStrict){
11715                 var ib = this.el.child('button');
11716                 if(ib && ib.getWidth() > 20){
11717                     ib.clip();
11718                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11719                 }
11720             }
11721             if(this.minWidth){
11722                 if(this.hidden){
11723                     this.el.beginMeasure();
11724                 }
11725                 if(this.el.getWidth() < this.minWidth){
11726                     this.el.setWidth(this.minWidth);
11727                 }
11728                 if(this.hidden){
11729                     this.el.endMeasure();
11730                 }
11731             }
11732         }
11733     },
11734
11735     /**
11736      * Assigns this button's click handler
11737      * @param {Function} handler The function to call when the button is clicked
11738      * @param {Object} scope (optional) Scope for the function passed in
11739      */
11740     setHandler : function(handler, scope){
11741         this.handler = handler;
11742         this.scope = scope;  
11743     },
11744     
11745     /**
11746      * Sets this button's text
11747      * @param {String} text The button text
11748      */
11749     setText : function(text){
11750         this.text = text;
11751         if(this.el){
11752             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11753         }
11754         this.autoWidth();
11755     },
11756     
11757     /**
11758      * Gets the text for this button
11759      * @return {String} The button text
11760      */
11761     getText : function(){
11762         return this.text;  
11763     },
11764     
11765     /**
11766      * Show this button
11767      */
11768     show: function(){
11769         this.hidden = false;
11770         if(this.el){
11771             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11772         }
11773     },
11774     
11775     /**
11776      * Hide this button
11777      */
11778     hide: function(){
11779         this.hidden = true;
11780         if(this.el){
11781             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11782         }
11783     },
11784     
11785     /**
11786      * Convenience function for boolean show/hide
11787      * @param {Boolean} visible True to show, false to hide
11788      */
11789     setVisible: function(visible){
11790         if(visible) {
11791             this.show();
11792         }else{
11793             this.hide();
11794         }
11795     },
11796     
11797     /**
11798      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11799      * @param {Boolean} state (optional) Force a particular state
11800      */
11801     toggle : function(state){
11802         state = state === undefined ? !this.pressed : state;
11803         if(state != this.pressed){
11804             if(state){
11805                 this.el.addClass("x-btn-pressed");
11806                 this.pressed = true;
11807                 this.fireEvent("toggle", this, true);
11808             }else{
11809                 this.el.removeClass("x-btn-pressed");
11810                 this.pressed = false;
11811                 this.fireEvent("toggle", this, false);
11812             }
11813             if(this.toggleHandler){
11814                 this.toggleHandler.call(this.scope || this, this, state);
11815             }
11816         }
11817     },
11818     
11819     /**
11820      * Focus the button
11821      */
11822     focus : function(){
11823         this.el.child('button:first').focus();
11824     },
11825     
11826     /**
11827      * Disable this button
11828      */
11829     disable : function(){
11830         if(this.el){
11831             this.el.addClass("x-btn-disabled");
11832         }
11833         this.disabled = true;
11834     },
11835     
11836     /**
11837      * Enable this button
11838      */
11839     enable : function(){
11840         if(this.el){
11841             this.el.removeClass("x-btn-disabled");
11842         }
11843         this.disabled = false;
11844     },
11845
11846     /**
11847      * Convenience function for boolean enable/disable
11848      * @param {Boolean} enabled True to enable, false to disable
11849      */
11850     setDisabled : function(v){
11851         this[v !== true ? "enable" : "disable"]();
11852     },
11853
11854     // private
11855     onClick : function(e){
11856         if(e){
11857             e.preventDefault();
11858         }
11859         if(e.button != 0){
11860             return;
11861         }
11862         if(!this.disabled){
11863             if(this.enableToggle){
11864                 this.toggle();
11865             }
11866             if(this.menu && !this.menu.isVisible()){
11867                 this.menu.show(this.el, this.menuAlign);
11868             }
11869             this.fireEvent("click", this, e);
11870             if(this.handler){
11871                 this.el.removeClass("x-btn-over");
11872                 this.handler.call(this.scope || this, this, e);
11873             }
11874         }
11875     },
11876     // private
11877     onMouseOver : function(e){
11878         if(!this.disabled){
11879             this.el.addClass("x-btn-over");
11880             this.fireEvent('mouseover', this, e);
11881         }
11882     },
11883     // private
11884     onMouseOut : function(e){
11885         if(!e.within(this.el,  true)){
11886             this.el.removeClass("x-btn-over");
11887             this.fireEvent('mouseout', this, e);
11888         }
11889     },
11890     // private
11891     onFocus : function(e){
11892         if(!this.disabled){
11893             this.el.addClass("x-btn-focus");
11894         }
11895     },
11896     // private
11897     onBlur : function(e){
11898         this.el.removeClass("x-btn-focus");
11899     },
11900     // private
11901     onMouseDown : function(e){
11902         if(!this.disabled && e.button == 0){
11903             this.el.addClass("x-btn-click");
11904             Roo.get(document).on('mouseup', this.onMouseUp, this);
11905         }
11906     },
11907     // private
11908     onMouseUp : function(e){
11909         if(e.button == 0){
11910             this.el.removeClass("x-btn-click");
11911             Roo.get(document).un('mouseup', this.onMouseUp, this);
11912         }
11913     },
11914     // private
11915     onMenuShow : function(e){
11916         this.el.addClass("x-btn-menu-active");
11917     },
11918     // private
11919     onMenuHide : function(e){
11920         this.el.removeClass("x-btn-menu-active");
11921     }   
11922 });
11923
11924 // Private utility class used by Button
11925 Roo.ButtonToggleMgr = function(){
11926    var groups = {};
11927    
11928    function toggleGroup(btn, state){
11929        if(state){
11930            var g = groups[btn.toggleGroup];
11931            for(var i = 0, l = g.length; i < l; i++){
11932                if(g[i] != btn){
11933                    g[i].toggle(false);
11934                }
11935            }
11936        }
11937    }
11938    
11939    return {
11940        register : function(btn){
11941            if(!btn.toggleGroup){
11942                return;
11943            }
11944            var g = groups[btn.toggleGroup];
11945            if(!g){
11946                g = groups[btn.toggleGroup] = [];
11947            }
11948            g.push(btn);
11949            btn.on("toggle", toggleGroup);
11950        },
11951        
11952        unregister : function(btn){
11953            if(!btn.toggleGroup){
11954                return;
11955            }
11956            var g = groups[btn.toggleGroup];
11957            if(g){
11958                g.remove(btn);
11959                btn.un("toggle", toggleGroup);
11960            }
11961        }
11962    };
11963 }();/*
11964  * Based on:
11965  * Ext JS Library 1.1.1
11966  * Copyright(c) 2006-2007, Ext JS, LLC.
11967  *
11968  * Originally Released Under LGPL - original licence link has changed is not relivant.
11969  *
11970  * Fork - LGPL
11971  * <script type="text/javascript">
11972  */
11973  
11974 /**
11975  * @class Roo.SplitButton
11976  * @extends Roo.Button
11977  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11978  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11979  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11980  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11981  * @cfg {String} arrowTooltip The title attribute of the arrow
11982  * @constructor
11983  * Create a new menu button
11984  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11985  * @param {Object} config The config object
11986  */
11987 Roo.SplitButton = function(renderTo, config){
11988     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11989     /**
11990      * @event arrowclick
11991      * Fires when this button's arrow is clicked
11992      * @param {SplitButton} this
11993      * @param {EventObject} e The click event
11994      */
11995     this.addEvents({"arrowclick":true});
11996 };
11997
11998 Roo.extend(Roo.SplitButton, Roo.Button, {
11999     render : function(renderTo){
12000         // this is one sweet looking template!
12001         var tpl = new Roo.Template(
12002             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12003             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12004             '<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>',
12005             "</tbody></table></td><td>",
12006             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12007             '<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>',
12008             "</tbody></table></td></tr></table>"
12009         );
12010         var btn = tpl.append(renderTo, [this.text, this.type], true);
12011         var btnEl = btn.child("button");
12012         if(this.cls){
12013             btn.addClass(this.cls);
12014         }
12015         if(this.icon){
12016             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12017         }
12018         if(this.iconCls){
12019             btnEl.addClass(this.iconCls);
12020             if(!this.cls){
12021                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12022             }
12023         }
12024         this.el = btn;
12025         if(this.handleMouseEvents){
12026             btn.on("mouseover", this.onMouseOver, this);
12027             btn.on("mouseout", this.onMouseOut, this);
12028             btn.on("mousedown", this.onMouseDown, this);
12029             btn.on("mouseup", this.onMouseUp, this);
12030         }
12031         btn.on(this.clickEvent, this.onClick, this);
12032         if(this.tooltip){
12033             if(typeof this.tooltip == 'object'){
12034                 Roo.QuickTips.tips(Roo.apply({
12035                       target: btnEl.id
12036                 }, this.tooltip));
12037             } else {
12038                 btnEl.dom[this.tooltipType] = this.tooltip;
12039             }
12040         }
12041         if(this.arrowTooltip){
12042             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12043         }
12044         if(this.hidden){
12045             this.hide();
12046         }
12047         if(this.disabled){
12048             this.disable();
12049         }
12050         if(this.pressed){
12051             this.el.addClass("x-btn-pressed");
12052         }
12053         if(Roo.isIE && !Roo.isIE7){
12054             this.autoWidth.defer(1, this);
12055         }else{
12056             this.autoWidth();
12057         }
12058         if(this.menu){
12059             this.menu.on("show", this.onMenuShow, this);
12060             this.menu.on("hide", this.onMenuHide, this);
12061         }
12062         this.fireEvent('render', this);
12063     },
12064
12065     // private
12066     autoWidth : function(){
12067         if(this.el){
12068             var tbl = this.el.child("table:first");
12069             var tbl2 = this.el.child("table:last");
12070             this.el.setWidth("auto");
12071             tbl.setWidth("auto");
12072             if(Roo.isIE7 && Roo.isStrict){
12073                 var ib = this.el.child('button:first');
12074                 if(ib && ib.getWidth() > 20){
12075                     ib.clip();
12076                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12077                 }
12078             }
12079             if(this.minWidth){
12080                 if(this.hidden){
12081                     this.el.beginMeasure();
12082                 }
12083                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12084                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12085                 }
12086                 if(this.hidden){
12087                     this.el.endMeasure();
12088                 }
12089             }
12090             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12091         } 
12092     },
12093     /**
12094      * Sets this button's click handler
12095      * @param {Function} handler The function to call when the button is clicked
12096      * @param {Object} scope (optional) Scope for the function passed above
12097      */
12098     setHandler : function(handler, scope){
12099         this.handler = handler;
12100         this.scope = scope;  
12101     },
12102     
12103     /**
12104      * Sets this button's arrow click handler
12105      * @param {Function} handler The function to call when the arrow is clicked
12106      * @param {Object} scope (optional) Scope for the function passed above
12107      */
12108     setArrowHandler : function(handler, scope){
12109         this.arrowHandler = handler;
12110         this.scope = scope;  
12111     },
12112     
12113     /**
12114      * Focus the button
12115      */
12116     focus : function(){
12117         if(this.el){
12118             this.el.child("button:first").focus();
12119         }
12120     },
12121
12122     // private
12123     onClick : function(e){
12124         e.preventDefault();
12125         if(!this.disabled){
12126             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12127                 if(this.menu && !this.menu.isVisible()){
12128                     this.menu.show(this.el, this.menuAlign);
12129                 }
12130                 this.fireEvent("arrowclick", this, e);
12131                 if(this.arrowHandler){
12132                     this.arrowHandler.call(this.scope || this, this, e);
12133                 }
12134             }else{
12135                 this.fireEvent("click", this, e);
12136                 if(this.handler){
12137                     this.handler.call(this.scope || this, this, e);
12138                 }
12139             }
12140         }
12141     },
12142     // private
12143     onMouseDown : function(e){
12144         if(!this.disabled){
12145             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12146         }
12147     },
12148     // private
12149     onMouseUp : function(e){
12150         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12151     }   
12152 });
12153
12154
12155 // backwards compat
12156 Roo.MenuButton = Roo.SplitButton;/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166
12167 /**
12168  * @class Roo.Toolbar
12169  * Basic Toolbar class.
12170  * @constructor
12171  * Creates a new Toolbar
12172  * @param {Object} config The config object
12173  */ 
12174 Roo.Toolbar = function(container, buttons, config)
12175 {
12176     /// old consturctor format still supported..
12177     if(container instanceof Array){ // omit the container for later rendering
12178         buttons = container;
12179         config = buttons;
12180         container = null;
12181     }
12182     if (typeof(container) == 'object' && container.xtype) {
12183         config = container;
12184         container = config.container;
12185         buttons = config.buttons; // not really - use items!!
12186     }
12187     var xitems = [];
12188     if (config && config.items) {
12189         xitems = config.items;
12190         delete config.items;
12191     }
12192     Roo.apply(this, config);
12193     this.buttons = buttons;
12194     
12195     if(container){
12196         this.render(container);
12197     }
12198     Roo.each(xitems, function(b) {
12199         this.add(b);
12200     }, this);
12201     
12202 };
12203
12204 Roo.Toolbar.prototype = {
12205     /**
12206      * @cfg {Roo.data.Store} items
12207      * array of button configs or elements to add
12208      */
12209     
12210     /**
12211      * @cfg {String/HTMLElement/Element} container
12212      * The id or element that will contain the toolbar
12213      */
12214     // private
12215     render : function(ct){
12216         this.el = Roo.get(ct);
12217         if(this.cls){
12218             this.el.addClass(this.cls);
12219         }
12220         // using a table allows for vertical alignment
12221         // 100% width is needed by Safari...
12222         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12223         this.tr = this.el.child("tr", true);
12224         var autoId = 0;
12225         this.items = new Roo.util.MixedCollection(false, function(o){
12226             return o.id || ("item" + (++autoId));
12227         });
12228         if(this.buttons){
12229             this.add.apply(this, this.buttons);
12230             delete this.buttons;
12231         }
12232     },
12233
12234     /**
12235      * Adds element(s) to the toolbar -- this function takes a variable number of 
12236      * arguments of mixed type and adds them to the toolbar.
12237      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12238      * <ul>
12239      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12240      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12241      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12242      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12243      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12244      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12245      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12246      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12247      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12248      * </ul>
12249      * @param {Mixed} arg2
12250      * @param {Mixed} etc.
12251      */
12252     add : function(){
12253         var a = arguments, l = a.length;
12254         for(var i = 0; i < l; i++){
12255             this._add(a[i]);
12256         }
12257     },
12258     // private..
12259     _add : function(el) {
12260         
12261         if (el.xtype) {
12262             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12263         }
12264         
12265         if (el.applyTo){ // some kind of form field
12266             return this.addField(el);
12267         } 
12268         if (el.render){ // some kind of Toolbar.Item
12269             return this.addItem(el);
12270         }
12271         if (typeof el == "string"){ // string
12272             if(el == "separator" || el == "-"){
12273                 return this.addSeparator();
12274             }
12275             if (el == " "){
12276                 return this.addSpacer();
12277             }
12278             if(el == "->"){
12279                 return this.addFill();
12280             }
12281             return this.addText(el);
12282             
12283         }
12284         if(el.tagName){ // element
12285             return this.addElement(el);
12286         }
12287         if(typeof el == "object"){ // must be button config?
12288             return this.addButton(el);
12289         }
12290         // and now what?!?!
12291         return false;
12292         
12293     },
12294     
12295     /**
12296      * Add an Xtype element
12297      * @param {Object} xtype Xtype Object
12298      * @return {Object} created Object
12299      */
12300     addxtype : function(e){
12301         return this.add(e);  
12302     },
12303     
12304     /**
12305      * Returns the Element for this toolbar.
12306      * @return {Roo.Element}
12307      */
12308     getEl : function(){
12309         return this.el;  
12310     },
12311     
12312     /**
12313      * Adds a separator
12314      * @return {Roo.Toolbar.Item} The separator item
12315      */
12316     addSeparator : function(){
12317         return this.addItem(new Roo.Toolbar.Separator());
12318     },
12319
12320     /**
12321      * Adds a spacer element
12322      * @return {Roo.Toolbar.Spacer} The spacer item
12323      */
12324     addSpacer : function(){
12325         return this.addItem(new Roo.Toolbar.Spacer());
12326     },
12327
12328     /**
12329      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12330      * @return {Roo.Toolbar.Fill} The fill item
12331      */
12332     addFill : function(){
12333         return this.addItem(new Roo.Toolbar.Fill());
12334     },
12335
12336     /**
12337      * Adds any standard HTML element to the toolbar
12338      * @param {String/HTMLElement/Element} el The element or id of the element to add
12339      * @return {Roo.Toolbar.Item} The element's item
12340      */
12341     addElement : function(el){
12342         return this.addItem(new Roo.Toolbar.Item(el));
12343     },
12344     /**
12345      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12346      * @type Roo.util.MixedCollection  
12347      */
12348     items : false,
12349      
12350     /**
12351      * Adds any Toolbar.Item or subclass
12352      * @param {Roo.Toolbar.Item} item
12353      * @return {Roo.Toolbar.Item} The item
12354      */
12355     addItem : function(item){
12356         var td = this.nextBlock();
12357         item.render(td);
12358         this.items.add(item);
12359         return item;
12360     },
12361     
12362     /**
12363      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12364      * @param {Object/Array} config A button config or array of configs
12365      * @return {Roo.Toolbar.Button/Array}
12366      */
12367     addButton : function(config){
12368         if(config instanceof Array){
12369             var buttons = [];
12370             for(var i = 0, len = config.length; i < len; i++) {
12371                 buttons.push(this.addButton(config[i]));
12372             }
12373             return buttons;
12374         }
12375         var b = config;
12376         if(!(config instanceof Roo.Toolbar.Button)){
12377             b = config.split ?
12378                 new Roo.Toolbar.SplitButton(config) :
12379                 new Roo.Toolbar.Button(config);
12380         }
12381         var td = this.nextBlock();
12382         b.render(td);
12383         this.items.add(b);
12384         return b;
12385     },
12386     
12387     /**
12388      * Adds text to the toolbar
12389      * @param {String} text The text to add
12390      * @return {Roo.Toolbar.Item} The element's item
12391      */
12392     addText : function(text){
12393         return this.addItem(new Roo.Toolbar.TextItem(text));
12394     },
12395     
12396     /**
12397      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12398      * @param {Number} index The index where the item is to be inserted
12399      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12400      * @return {Roo.Toolbar.Button/Item}
12401      */
12402     insertButton : function(index, item){
12403         if(item instanceof Array){
12404             var buttons = [];
12405             for(var i = 0, len = item.length; i < len; i++) {
12406                buttons.push(this.insertButton(index + i, item[i]));
12407             }
12408             return buttons;
12409         }
12410         if (!(item instanceof Roo.Toolbar.Button)){
12411            item = new Roo.Toolbar.Button(item);
12412         }
12413         var td = document.createElement("td");
12414         this.tr.insertBefore(td, this.tr.childNodes[index]);
12415         item.render(td);
12416         this.items.insert(index, item);
12417         return item;
12418     },
12419     
12420     /**
12421      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12422      * @param {Object} config
12423      * @return {Roo.Toolbar.Item} The element's item
12424      */
12425     addDom : function(config, returnEl){
12426         var td = this.nextBlock();
12427         Roo.DomHelper.overwrite(td, config);
12428         var ti = new Roo.Toolbar.Item(td.firstChild);
12429         ti.render(td);
12430         this.items.add(ti);
12431         return ti;
12432     },
12433
12434     /**
12435      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12436      * @type Roo.util.MixedCollection  
12437      */
12438     fields : false,
12439     
12440     /**
12441      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12442      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12443      * @param {Roo.form.Field} field
12444      * @return {Roo.ToolbarItem}
12445      */
12446      
12447       
12448     addField : function(field) {
12449         if (!this.fields) {
12450             var autoId = 0;
12451             this.fields = new Roo.util.MixedCollection(false, function(o){
12452                 return o.id || ("item" + (++autoId));
12453             });
12454
12455         }
12456         
12457         var td = this.nextBlock();
12458         field.render(td);
12459         var ti = new Roo.Toolbar.Item(td.firstChild);
12460         ti.render(td);
12461         this.items.add(ti);
12462         this.fields.add(field);
12463         return ti;
12464     },
12465     /**
12466      * Hide the toolbar
12467      * @method hide
12468      */
12469      
12470       
12471     hide : function()
12472     {
12473         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12474         this.el.child('div').hide();
12475     },
12476     /**
12477      * Show the toolbar
12478      * @method show
12479      */
12480     show : function()
12481     {
12482         this.el.child('div').show();
12483     },
12484       
12485     // private
12486     nextBlock : function(){
12487         var td = document.createElement("td");
12488         this.tr.appendChild(td);
12489         return td;
12490     },
12491
12492     // private
12493     destroy : function(){
12494         if(this.items){ // rendered?
12495             Roo.destroy.apply(Roo, this.items.items);
12496         }
12497         if(this.fields){ // rendered?
12498             Roo.destroy.apply(Roo, this.fields.items);
12499         }
12500         Roo.Element.uncache(this.el, this.tr);
12501     }
12502 };
12503
12504 /**
12505  * @class Roo.Toolbar.Item
12506  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12507  * @constructor
12508  * Creates a new Item
12509  * @param {HTMLElement} el 
12510  */
12511 Roo.Toolbar.Item = function(el){
12512     this.el = Roo.getDom(el);
12513     this.id = Roo.id(this.el);
12514     this.hidden = false;
12515 };
12516
12517 Roo.Toolbar.Item.prototype = {
12518     
12519     /**
12520      * Get this item's HTML Element
12521      * @return {HTMLElement}
12522      */
12523     getEl : function(){
12524        return this.el;  
12525     },
12526
12527     // private
12528     render : function(td){
12529         this.td = td;
12530         td.appendChild(this.el);
12531     },
12532     
12533     /**
12534      * Removes and destroys this item.
12535      */
12536     destroy : function(){
12537         this.td.parentNode.removeChild(this.td);
12538     },
12539     
12540     /**
12541      * Shows this item.
12542      */
12543     show: function(){
12544         this.hidden = false;
12545         this.td.style.display = "";
12546     },
12547     
12548     /**
12549      * Hides this item.
12550      */
12551     hide: function(){
12552         this.hidden = true;
12553         this.td.style.display = "none";
12554     },
12555     
12556     /**
12557      * Convenience function for boolean show/hide.
12558      * @param {Boolean} visible true to show/false to hide
12559      */
12560     setVisible: function(visible){
12561         if(visible) {
12562             this.show();
12563         }else{
12564             this.hide();
12565         }
12566     },
12567     
12568     /**
12569      * Try to focus this item.
12570      */
12571     focus : function(){
12572         Roo.fly(this.el).focus();
12573     },
12574     
12575     /**
12576      * Disables this item.
12577      */
12578     disable : function(){
12579         Roo.fly(this.td).addClass("x-item-disabled");
12580         this.disabled = true;
12581         this.el.disabled = true;
12582     },
12583     
12584     /**
12585      * Enables this item.
12586      */
12587     enable : function(){
12588         Roo.fly(this.td).removeClass("x-item-disabled");
12589         this.disabled = false;
12590         this.el.disabled = false;
12591     }
12592 };
12593
12594
12595 /**
12596  * @class Roo.Toolbar.Separator
12597  * @extends Roo.Toolbar.Item
12598  * A simple toolbar separator class
12599  * @constructor
12600  * Creates a new Separator
12601  */
12602 Roo.Toolbar.Separator = function(){
12603     var s = document.createElement("span");
12604     s.className = "ytb-sep";
12605     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12606 };
12607 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12608     enable:Roo.emptyFn,
12609     disable:Roo.emptyFn,
12610     focus:Roo.emptyFn
12611 });
12612
12613 /**
12614  * @class Roo.Toolbar.Spacer
12615  * @extends Roo.Toolbar.Item
12616  * A simple element that adds extra horizontal space to a toolbar.
12617  * @constructor
12618  * Creates a new Spacer
12619  */
12620 Roo.Toolbar.Spacer = function(){
12621     var s = document.createElement("div");
12622     s.className = "ytb-spacer";
12623     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12624 };
12625 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12626     enable:Roo.emptyFn,
12627     disable:Roo.emptyFn,
12628     focus:Roo.emptyFn
12629 });
12630
12631 /**
12632  * @class Roo.Toolbar.Fill
12633  * @extends Roo.Toolbar.Spacer
12634  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12635  * @constructor
12636  * Creates a new Spacer
12637  */
12638 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12639     // private
12640     render : function(td){
12641         td.style.width = '100%';
12642         Roo.Toolbar.Fill.superclass.render.call(this, td);
12643     }
12644 });
12645
12646 /**
12647  * @class Roo.Toolbar.TextItem
12648  * @extends Roo.Toolbar.Item
12649  * A simple class that renders text directly into a toolbar.
12650  * @constructor
12651  * Creates a new TextItem
12652  * @param {String} text
12653  */
12654 Roo.Toolbar.TextItem = function(text){
12655     if (typeof(text) == 'object') {
12656         text = text.text;
12657     }
12658     var s = document.createElement("span");
12659     s.className = "ytb-text";
12660     s.innerHTML = text;
12661     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12662 };
12663 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12664     enable:Roo.emptyFn,
12665     disable:Roo.emptyFn,
12666     focus:Roo.emptyFn
12667 });
12668
12669 /**
12670  * @class Roo.Toolbar.Button
12671  * @extends Roo.Button
12672  * A button that renders into a toolbar.
12673  * @constructor
12674  * Creates a new Button
12675  * @param {Object} config A standard {@link Roo.Button} config object
12676  */
12677 Roo.Toolbar.Button = function(config){
12678     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12679 };
12680 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12681     render : function(td){
12682         this.td = td;
12683         Roo.Toolbar.Button.superclass.render.call(this, td);
12684     },
12685     
12686     /**
12687      * Removes and destroys this button
12688      */
12689     destroy : function(){
12690         Roo.Toolbar.Button.superclass.destroy.call(this);
12691         this.td.parentNode.removeChild(this.td);
12692     },
12693     
12694     /**
12695      * Shows this button
12696      */
12697     show: function(){
12698         this.hidden = false;
12699         this.td.style.display = "";
12700     },
12701     
12702     /**
12703      * Hides this button
12704      */
12705     hide: function(){
12706         this.hidden = true;
12707         this.td.style.display = "none";
12708     },
12709
12710     /**
12711      * Disables this item
12712      */
12713     disable : function(){
12714         Roo.fly(this.td).addClass("x-item-disabled");
12715         this.disabled = true;
12716     },
12717
12718     /**
12719      * Enables this item
12720      */
12721     enable : function(){
12722         Roo.fly(this.td).removeClass("x-item-disabled");
12723         this.disabled = false;
12724     }
12725 });
12726 // backwards compat
12727 Roo.ToolbarButton = Roo.Toolbar.Button;
12728
12729 /**
12730  * @class Roo.Toolbar.SplitButton
12731  * @extends Roo.SplitButton
12732  * A menu button that renders into a toolbar.
12733  * @constructor
12734  * Creates a new SplitButton
12735  * @param {Object} config A standard {@link Roo.SplitButton} config object
12736  */
12737 Roo.Toolbar.SplitButton = function(config){
12738     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12739 };
12740 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12741     render : function(td){
12742         this.td = td;
12743         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12744     },
12745     
12746     /**
12747      * Removes and destroys this button
12748      */
12749     destroy : function(){
12750         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12751         this.td.parentNode.removeChild(this.td);
12752     },
12753     
12754     /**
12755      * Shows this button
12756      */
12757     show: function(){
12758         this.hidden = false;
12759         this.td.style.display = "";
12760     },
12761     
12762     /**
12763      * Hides this button
12764      */
12765     hide: function(){
12766         this.hidden = true;
12767         this.td.style.display = "none";
12768     }
12769 });
12770
12771 // backwards compat
12772 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12773  * Based on:
12774  * Ext JS Library 1.1.1
12775  * Copyright(c) 2006-2007, Ext JS, LLC.
12776  *
12777  * Originally Released Under LGPL - original licence link has changed is not relivant.
12778  *
12779  * Fork - LGPL
12780  * <script type="text/javascript">
12781  */
12782  
12783 /**
12784  * @class Roo.PagingToolbar
12785  * @extends Roo.Toolbar
12786  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12787  * @constructor
12788  * Create a new PagingToolbar
12789  * @param {Object} config The config object
12790  */
12791 Roo.PagingToolbar = function(el, ds, config)
12792 {
12793     // old args format still supported... - xtype is prefered..
12794     if (typeof(el) == 'object' && el.xtype) {
12795         // created from xtype...
12796         config = el;
12797         ds = el.dataSource;
12798         el = config.container;
12799     }
12800     var items = [];
12801     if (config.items) {
12802         items = config.items;
12803         config.items = [];
12804     }
12805     
12806     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12807     this.ds = ds;
12808     this.cursor = 0;
12809     this.renderButtons(this.el);
12810     this.bind(ds);
12811     
12812     // supprot items array.
12813    
12814     Roo.each(items, function(e) {
12815         this.add(Roo.factory(e));
12816     },this);
12817     
12818 };
12819
12820 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12821     /**
12822      * @cfg {Roo.data.Store} dataSource
12823      * The underlying data store providing the paged data
12824      */
12825     /**
12826      * @cfg {String/HTMLElement/Element} container
12827      * container The id or element that will contain the toolbar
12828      */
12829     /**
12830      * @cfg {Boolean} displayInfo
12831      * True to display the displayMsg (defaults to false)
12832      */
12833     /**
12834      * @cfg {Number} pageSize
12835      * The number of records to display per page (defaults to 20)
12836      */
12837     pageSize: 20,
12838     /**
12839      * @cfg {String} displayMsg
12840      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12841      */
12842     displayMsg : 'Displaying {0} - {1} of {2}',
12843     /**
12844      * @cfg {String} emptyMsg
12845      * The message to display when no records are found (defaults to "No data to display")
12846      */
12847     emptyMsg : 'No data to display',
12848     /**
12849      * Customizable piece of the default paging text (defaults to "Page")
12850      * @type String
12851      */
12852     beforePageText : "Page",
12853     /**
12854      * Customizable piece of the default paging text (defaults to "of %0")
12855      * @type String
12856      */
12857     afterPageText : "of {0}",
12858     /**
12859      * Customizable piece of the default paging text (defaults to "First Page")
12860      * @type String
12861      */
12862     firstText : "First Page",
12863     /**
12864      * Customizable piece of the default paging text (defaults to "Previous Page")
12865      * @type String
12866      */
12867     prevText : "Previous Page",
12868     /**
12869      * Customizable piece of the default paging text (defaults to "Next Page")
12870      * @type String
12871      */
12872     nextText : "Next Page",
12873     /**
12874      * Customizable piece of the default paging text (defaults to "Last Page")
12875      * @type String
12876      */
12877     lastText : "Last Page",
12878     /**
12879      * Customizable piece of the default paging text (defaults to "Refresh")
12880      * @type String
12881      */
12882     refreshText : "Refresh",
12883
12884     // private
12885     renderButtons : function(el){
12886         Roo.PagingToolbar.superclass.render.call(this, el);
12887         this.first = this.addButton({
12888             tooltip: this.firstText,
12889             cls: "x-btn-icon x-grid-page-first",
12890             disabled: true,
12891             handler: this.onClick.createDelegate(this, ["first"])
12892         });
12893         this.prev = this.addButton({
12894             tooltip: this.prevText,
12895             cls: "x-btn-icon x-grid-page-prev",
12896             disabled: true,
12897             handler: this.onClick.createDelegate(this, ["prev"])
12898         });
12899         //this.addSeparator();
12900         this.add(this.beforePageText);
12901         this.field = Roo.get(this.addDom({
12902            tag: "input",
12903            type: "text",
12904            size: "3",
12905            value: "1",
12906            cls: "x-grid-page-number"
12907         }).el);
12908         this.field.on("keydown", this.onPagingKeydown, this);
12909         this.field.on("focus", function(){this.dom.select();});
12910         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12911         this.field.setHeight(18);
12912         //this.addSeparator();
12913         this.next = this.addButton({
12914             tooltip: this.nextText,
12915             cls: "x-btn-icon x-grid-page-next",
12916             disabled: true,
12917             handler: this.onClick.createDelegate(this, ["next"])
12918         });
12919         this.last = this.addButton({
12920             tooltip: this.lastText,
12921             cls: "x-btn-icon x-grid-page-last",
12922             disabled: true,
12923             handler: this.onClick.createDelegate(this, ["last"])
12924         });
12925         //this.addSeparator();
12926         this.loading = this.addButton({
12927             tooltip: this.refreshText,
12928             cls: "x-btn-icon x-grid-loading",
12929             handler: this.onClick.createDelegate(this, ["refresh"])
12930         });
12931
12932         if(this.displayInfo){
12933             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12934         }
12935     },
12936
12937     // private
12938     updateInfo : function(){
12939         if(this.displayEl){
12940             var count = this.ds.getCount();
12941             var msg = count == 0 ?
12942                 this.emptyMsg :
12943                 String.format(
12944                     this.displayMsg,
12945                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12946                 );
12947             this.displayEl.update(msg);
12948         }
12949     },
12950
12951     // private
12952     onLoad : function(ds, r, o){
12953        this.cursor = o.params ? o.params.start : 0;
12954        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12955
12956        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12957        this.field.dom.value = ap;
12958        this.first.setDisabled(ap == 1);
12959        this.prev.setDisabled(ap == 1);
12960        this.next.setDisabled(ap == ps);
12961        this.last.setDisabled(ap == ps);
12962        this.loading.enable();
12963        this.updateInfo();
12964     },
12965
12966     // private
12967     getPageData : function(){
12968         var total = this.ds.getTotalCount();
12969         return {
12970             total : total,
12971             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12972             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12973         };
12974     },
12975
12976     // private
12977     onLoadError : function(){
12978         this.loading.enable();
12979     },
12980
12981     // private
12982     onPagingKeydown : function(e){
12983         var k = e.getKey();
12984         var d = this.getPageData();
12985         if(k == e.RETURN){
12986             var v = this.field.dom.value, pageNum;
12987             if(!v || isNaN(pageNum = parseInt(v, 10))){
12988                 this.field.dom.value = d.activePage;
12989                 return;
12990             }
12991             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12992             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12993             e.stopEvent();
12994         }
12995         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))
12996         {
12997           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12998           this.field.dom.value = pageNum;
12999           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13000           e.stopEvent();
13001         }
13002         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13003         {
13004           var v = this.field.dom.value, pageNum; 
13005           var increment = (e.shiftKey) ? 10 : 1;
13006           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13007             increment *= -1;
13008           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13009             this.field.dom.value = d.activePage;
13010             return;
13011           }
13012           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13013           {
13014             this.field.dom.value = parseInt(v, 10) + increment;
13015             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13016             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13017           }
13018           e.stopEvent();
13019         }
13020     },
13021
13022     // private
13023     beforeLoad : function(){
13024         if(this.loading){
13025             this.loading.disable();
13026         }
13027     },
13028
13029     // private
13030     onClick : function(which){
13031         var ds = this.ds;
13032         switch(which){
13033             case "first":
13034                 ds.load({params:{start: 0, limit: this.pageSize}});
13035             break;
13036             case "prev":
13037                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13038             break;
13039             case "next":
13040                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13041             break;
13042             case "last":
13043                 var total = ds.getTotalCount();
13044                 var extra = total % this.pageSize;
13045                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13046                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13047             break;
13048             case "refresh":
13049                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13050             break;
13051         }
13052     },
13053
13054     /**
13055      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13056      * @param {Roo.data.Store} store The data store to unbind
13057      */
13058     unbind : function(ds){
13059         ds.un("beforeload", this.beforeLoad, this);
13060         ds.un("load", this.onLoad, this);
13061         ds.un("loadexception", this.onLoadError, this);
13062         ds.un("remove", this.updateInfo, this);
13063         ds.un("add", this.updateInfo, this);
13064         this.ds = undefined;
13065     },
13066
13067     /**
13068      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13069      * @param {Roo.data.Store} store The data store to bind
13070      */
13071     bind : function(ds){
13072         ds.on("beforeload", this.beforeLoad, this);
13073         ds.on("load", this.onLoad, this);
13074         ds.on("loadexception", this.onLoadError, this);
13075         ds.on("remove", this.updateInfo, this);
13076         ds.on("add", this.updateInfo, this);
13077         this.ds = ds;
13078     }
13079 });/*
13080  * Based on:
13081  * Ext JS Library 1.1.1
13082  * Copyright(c) 2006-2007, Ext JS, LLC.
13083  *
13084  * Originally Released Under LGPL - original licence link has changed is not relivant.
13085  *
13086  * Fork - LGPL
13087  * <script type="text/javascript">
13088  */
13089
13090 /**
13091  * @class Roo.Resizable
13092  * @extends Roo.util.Observable
13093  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13094  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13095  * 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
13096  * the element will be wrapped for you automatically.</p>
13097  * <p>Here is the list of valid resize handles:</p>
13098  * <pre>
13099 Value   Description
13100 ------  -------------------
13101  'n'     north
13102  's'     south
13103  'e'     east
13104  'w'     west
13105  'nw'    northwest
13106  'sw'    southwest
13107  'se'    southeast
13108  'ne'    northeast
13109  'hd'    horizontal drag
13110  'all'   all
13111 </pre>
13112  * <p>Here's an example showing the creation of a typical Resizable:</p>
13113  * <pre><code>
13114 var resizer = new Roo.Resizable("element-id", {
13115     handles: 'all',
13116     minWidth: 200,
13117     minHeight: 100,
13118     maxWidth: 500,
13119     maxHeight: 400,
13120     pinned: true
13121 });
13122 resizer.on("resize", myHandler);
13123 </code></pre>
13124  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13125  * resizer.east.setDisplayed(false);</p>
13126  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13127  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13128  * resize operation's new size (defaults to [0, 0])
13129  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13130  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13131  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13132  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13133  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13134  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13135  * @cfg {Number} width The width of the element in pixels (defaults to null)
13136  * @cfg {Number} height The height of the element in pixels (defaults to null)
13137  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13138  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13139  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13140  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13141  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13142  * in favor of the handles config option (defaults to false)
13143  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13144  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13145  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13146  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13147  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13148  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13149  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13150  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13151  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13152  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13153  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13154  * @constructor
13155  * Create a new resizable component
13156  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13157  * @param {Object} config configuration options
13158   */
13159 Roo.Resizable = function(el, config)
13160 {
13161     this.el = Roo.get(el);
13162
13163     if(config && config.wrap){
13164         config.resizeChild = this.el;
13165         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13166         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13167         this.el.setStyle("overflow", "hidden");
13168         this.el.setPositioning(config.resizeChild.getPositioning());
13169         config.resizeChild.clearPositioning();
13170         if(!config.width || !config.height){
13171             var csize = config.resizeChild.getSize();
13172             this.el.setSize(csize.width, csize.height);
13173         }
13174         if(config.pinned && !config.adjustments){
13175             config.adjustments = "auto";
13176         }
13177     }
13178
13179     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13180     this.proxy.unselectable();
13181     this.proxy.enableDisplayMode('block');
13182
13183     Roo.apply(this, config);
13184
13185     if(this.pinned){
13186         this.disableTrackOver = true;
13187         this.el.addClass("x-resizable-pinned");
13188     }
13189     // if the element isn't positioned, make it relative
13190     var position = this.el.getStyle("position");
13191     if(position != "absolute" && position != "fixed"){
13192         this.el.setStyle("position", "relative");
13193     }
13194     if(!this.handles){ // no handles passed, must be legacy style
13195         this.handles = 's,e,se';
13196         if(this.multiDirectional){
13197             this.handles += ',n,w';
13198         }
13199     }
13200     if(this.handles == "all"){
13201         this.handles = "n s e w ne nw se sw";
13202     }
13203     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13204     var ps = Roo.Resizable.positions;
13205     for(var i = 0, len = hs.length; i < len; i++){
13206         if(hs[i] && ps[hs[i]]){
13207             var pos = ps[hs[i]];
13208             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13209         }
13210     }
13211     // legacy
13212     this.corner = this.southeast;
13213     
13214     // updateBox = the box can move..
13215     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13216         this.updateBox = true;
13217     }
13218
13219     this.activeHandle = null;
13220
13221     if(this.resizeChild){
13222         if(typeof this.resizeChild == "boolean"){
13223             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13224         }else{
13225             this.resizeChild = Roo.get(this.resizeChild, true);
13226         }
13227     }
13228     
13229     if(this.adjustments == "auto"){
13230         var rc = this.resizeChild;
13231         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13232         if(rc && (hw || hn)){
13233             rc.position("relative");
13234             rc.setLeft(hw ? hw.el.getWidth() : 0);
13235             rc.setTop(hn ? hn.el.getHeight() : 0);
13236         }
13237         this.adjustments = [
13238             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13239             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13240         ];
13241     }
13242
13243     if(this.draggable){
13244         this.dd = this.dynamic ?
13245             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13246         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13247     }
13248
13249     // public events
13250     this.addEvents({
13251         /**
13252          * @event beforeresize
13253          * Fired before resize is allowed. Set enabled to false to cancel resize.
13254          * @param {Roo.Resizable} this
13255          * @param {Roo.EventObject} e The mousedown event
13256          */
13257         "beforeresize" : true,
13258         /**
13259          * @event resize
13260          * Fired after a resize.
13261          * @param {Roo.Resizable} this
13262          * @param {Number} width The new width
13263          * @param {Number} height The new height
13264          * @param {Roo.EventObject} e The mouseup event
13265          */
13266         "resize" : true
13267     });
13268
13269     if(this.width !== null && this.height !== null){
13270         this.resizeTo(this.width, this.height);
13271     }else{
13272         this.updateChildSize();
13273     }
13274     if(Roo.isIE){
13275         this.el.dom.style.zoom = 1;
13276     }
13277     Roo.Resizable.superclass.constructor.call(this);
13278 };
13279
13280 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13281         resizeChild : false,
13282         adjustments : [0, 0],
13283         minWidth : 5,
13284         minHeight : 5,
13285         maxWidth : 10000,
13286         maxHeight : 10000,
13287         enabled : true,
13288         animate : false,
13289         duration : .35,
13290         dynamic : false,
13291         handles : false,
13292         multiDirectional : false,
13293         disableTrackOver : false,
13294         easing : 'easeOutStrong',
13295         widthIncrement : 0,
13296         heightIncrement : 0,
13297         pinned : false,
13298         width : null,
13299         height : null,
13300         preserveRatio : false,
13301         transparent: false,
13302         minX: 0,
13303         minY: 0,
13304         draggable: false,
13305
13306         /**
13307          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13308          */
13309         constrainTo: undefined,
13310         /**
13311          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13312          */
13313         resizeRegion: undefined,
13314
13315
13316     /**
13317      * Perform a manual resize
13318      * @param {Number} width
13319      * @param {Number} height
13320      */
13321     resizeTo : function(width, height){
13322         this.el.setSize(width, height);
13323         this.updateChildSize();
13324         this.fireEvent("resize", this, width, height, null);
13325     },
13326
13327     // private
13328     startSizing : function(e, handle){
13329         this.fireEvent("beforeresize", this, e);
13330         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13331
13332             if(!this.overlay){
13333                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13334                 this.overlay.unselectable();
13335                 this.overlay.enableDisplayMode("block");
13336                 this.overlay.on("mousemove", this.onMouseMove, this);
13337                 this.overlay.on("mouseup", this.onMouseUp, this);
13338             }
13339             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13340
13341             this.resizing = true;
13342             this.startBox = this.el.getBox();
13343             this.startPoint = e.getXY();
13344             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13345                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13346
13347             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13348             this.overlay.show();
13349
13350             if(this.constrainTo) {
13351                 var ct = Roo.get(this.constrainTo);
13352                 this.resizeRegion = ct.getRegion().adjust(
13353                     ct.getFrameWidth('t'),
13354                     ct.getFrameWidth('l'),
13355                     -ct.getFrameWidth('b'),
13356                     -ct.getFrameWidth('r')
13357                 );
13358             }
13359
13360             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13361             this.proxy.show();
13362             this.proxy.setBox(this.startBox);
13363             if(!this.dynamic){
13364                 this.proxy.setStyle('visibility', 'visible');
13365             }
13366         }
13367     },
13368
13369     // private
13370     onMouseDown : function(handle, e){
13371         if(this.enabled){
13372             e.stopEvent();
13373             this.activeHandle = handle;
13374             this.startSizing(e, handle);
13375         }
13376     },
13377
13378     // private
13379     onMouseUp : function(e){
13380         var size = this.resizeElement();
13381         this.resizing = false;
13382         this.handleOut();
13383         this.overlay.hide();
13384         this.proxy.hide();
13385         this.fireEvent("resize", this, size.width, size.height, e);
13386     },
13387
13388     // private
13389     updateChildSize : function(){
13390         if(this.resizeChild){
13391             var el = this.el;
13392             var child = this.resizeChild;
13393             var adj = this.adjustments;
13394             if(el.dom.offsetWidth){
13395                 var b = el.getSize(true);
13396                 child.setSize(b.width+adj[0], b.height+adj[1]);
13397             }
13398             // Second call here for IE
13399             // The first call enables instant resizing and
13400             // the second call corrects scroll bars if they
13401             // exist
13402             if(Roo.isIE){
13403                 setTimeout(function(){
13404                     if(el.dom.offsetWidth){
13405                         var b = el.getSize(true);
13406                         child.setSize(b.width+adj[0], b.height+adj[1]);
13407                     }
13408                 }, 10);
13409             }
13410         }
13411     },
13412
13413     // private
13414     snap : function(value, inc, min){
13415         if(!inc || !value) return value;
13416         var newValue = value;
13417         var m = value % inc;
13418         if(m > 0){
13419             if(m > (inc/2)){
13420                 newValue = value + (inc-m);
13421             }else{
13422                 newValue = value - m;
13423             }
13424         }
13425         return Math.max(min, newValue);
13426     },
13427
13428     // private
13429     resizeElement : function(){
13430         var box = this.proxy.getBox();
13431         if(this.updateBox){
13432             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13433         }else{
13434             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13435         }
13436         this.updateChildSize();
13437         if(!this.dynamic){
13438             this.proxy.hide();
13439         }
13440         return box;
13441     },
13442
13443     // private
13444     constrain : function(v, diff, m, mx){
13445         if(v - diff < m){
13446             diff = v - m;
13447         }else if(v - diff > mx){
13448             diff = mx - v;
13449         }
13450         return diff;
13451     },
13452
13453     // private
13454     onMouseMove : function(e){
13455         if(this.enabled){
13456             try{// try catch so if something goes wrong the user doesn't get hung
13457
13458             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13459                 return;
13460             }
13461
13462             //var curXY = this.startPoint;
13463             var curSize = this.curSize || this.startBox;
13464             var x = this.startBox.x, y = this.startBox.y;
13465             var ox = x, oy = y;
13466             var w = curSize.width, h = curSize.height;
13467             var ow = w, oh = h;
13468             var mw = this.minWidth, mh = this.minHeight;
13469             var mxw = this.maxWidth, mxh = this.maxHeight;
13470             var wi = this.widthIncrement;
13471             var hi = this.heightIncrement;
13472
13473             var eventXY = e.getXY();
13474             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13475             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13476
13477             var pos = this.activeHandle.position;
13478
13479             switch(pos){
13480                 case "east":
13481                     w += diffX;
13482                     w = Math.min(Math.max(mw, w), mxw);
13483                     break;
13484              
13485                 case "south":
13486                     h += diffY;
13487                     h = Math.min(Math.max(mh, h), mxh);
13488                     break;
13489                 case "southeast":
13490                     w += diffX;
13491                     h += diffY;
13492                     w = Math.min(Math.max(mw, w), mxw);
13493                     h = Math.min(Math.max(mh, h), mxh);
13494                     break;
13495                 case "north":
13496                     diffY = this.constrain(h, diffY, mh, mxh);
13497                     y += diffY;
13498                     h -= diffY;
13499                     break;
13500                 case "hdrag":
13501                     
13502                     if (wi) {
13503                         var adiffX = Math.abs(diffX);
13504                         var sub = (adiffX % wi); // how much 
13505                         if (sub > (wi/2)) { // far enough to snap
13506                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13507                         } else {
13508                             // remove difference.. 
13509                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13510                         }
13511                     }
13512                     x += diffX;
13513                     x = Math.max(this.minX, x);
13514                     break;
13515                 case "west":
13516                     diffX = this.constrain(w, diffX, mw, mxw);
13517                     x += diffX;
13518                     w -= diffX;
13519                     break;
13520                 case "northeast":
13521                     w += diffX;
13522                     w = Math.min(Math.max(mw, w), mxw);
13523                     diffY = this.constrain(h, diffY, mh, mxh);
13524                     y += diffY;
13525                     h -= diffY;
13526                     break;
13527                 case "northwest":
13528                     diffX = this.constrain(w, diffX, mw, mxw);
13529                     diffY = this.constrain(h, diffY, mh, mxh);
13530                     y += diffY;
13531                     h -= diffY;
13532                     x += diffX;
13533                     w -= diffX;
13534                     break;
13535                case "southwest":
13536                     diffX = this.constrain(w, diffX, mw, mxw);
13537                     h += diffY;
13538                     h = Math.min(Math.max(mh, h), mxh);
13539                     x += diffX;
13540                     w -= diffX;
13541                     break;
13542             }
13543
13544             var sw = this.snap(w, wi, mw);
13545             var sh = this.snap(h, hi, mh);
13546             if(sw != w || sh != h){
13547                 switch(pos){
13548                     case "northeast":
13549                         y -= sh - h;
13550                     break;
13551                     case "north":
13552                         y -= sh - h;
13553                         break;
13554                     case "southwest":
13555                         x -= sw - w;
13556                     break;
13557                     case "west":
13558                         x -= sw - w;
13559                         break;
13560                     case "northwest":
13561                         x -= sw - w;
13562                         y -= sh - h;
13563                     break;
13564                 }
13565                 w = sw;
13566                 h = sh;
13567             }
13568
13569             if(this.preserveRatio){
13570                 switch(pos){
13571                     case "southeast":
13572                     case "east":
13573                         h = oh * (w/ow);
13574                         h = Math.min(Math.max(mh, h), mxh);
13575                         w = ow * (h/oh);
13576                        break;
13577                     case "south":
13578                         w = ow * (h/oh);
13579                         w = Math.min(Math.max(mw, w), mxw);
13580                         h = oh * (w/ow);
13581                         break;
13582                     case "northeast":
13583                         w = ow * (h/oh);
13584                         w = Math.min(Math.max(mw, w), mxw);
13585                         h = oh * (w/ow);
13586                     break;
13587                     case "north":
13588                         var tw = w;
13589                         w = ow * (h/oh);
13590                         w = Math.min(Math.max(mw, w), mxw);
13591                         h = oh * (w/ow);
13592                         x += (tw - w) / 2;
13593                         break;
13594                     case "southwest":
13595                         h = oh * (w/ow);
13596                         h = Math.min(Math.max(mh, h), mxh);
13597                         var tw = w;
13598                         w = ow * (h/oh);
13599                         x += tw - w;
13600                         break;
13601                     case "west":
13602                         var th = h;
13603                         h = oh * (w/ow);
13604                         h = Math.min(Math.max(mh, h), mxh);
13605                         y += (th - h) / 2;
13606                         var tw = w;
13607                         w = ow * (h/oh);
13608                         x += tw - w;
13609                        break;
13610                     case "northwest":
13611                         var tw = w;
13612                         var th = h;
13613                         h = oh * (w/ow);
13614                         h = Math.min(Math.max(mh, h), mxh);
13615                         w = ow * (h/oh);
13616                         y += th - h;
13617                         x += tw - w;
13618                        break;
13619
13620                 }
13621             }
13622             if (pos == 'hdrag') {
13623                 w = ow;
13624             }
13625             this.proxy.setBounds(x, y, w, h);
13626             if(this.dynamic){
13627                 this.resizeElement();
13628             }
13629             }catch(e){}
13630         }
13631     },
13632
13633     // private
13634     handleOver : function(){
13635         if(this.enabled){
13636             this.el.addClass("x-resizable-over");
13637         }
13638     },
13639
13640     // private
13641     handleOut : function(){
13642         if(!this.resizing){
13643             this.el.removeClass("x-resizable-over");
13644         }
13645     },
13646
13647     /**
13648      * Returns the element this component is bound to.
13649      * @return {Roo.Element}
13650      */
13651     getEl : function(){
13652         return this.el;
13653     },
13654
13655     /**
13656      * Returns the resizeChild element (or null).
13657      * @return {Roo.Element}
13658      */
13659     getResizeChild : function(){
13660         return this.resizeChild;
13661     },
13662
13663     /**
13664      * Destroys this resizable. If the element was wrapped and
13665      * removeEl is not true then the element remains.
13666      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13667      */
13668     destroy : function(removeEl){
13669         this.proxy.remove();
13670         if(this.overlay){
13671             this.overlay.removeAllListeners();
13672             this.overlay.remove();
13673         }
13674         var ps = Roo.Resizable.positions;
13675         for(var k in ps){
13676             if(typeof ps[k] != "function" && this[ps[k]]){
13677                 var h = this[ps[k]];
13678                 h.el.removeAllListeners();
13679                 h.el.remove();
13680             }
13681         }
13682         if(removeEl){
13683             this.el.update("");
13684             this.el.remove();
13685         }
13686     }
13687 });
13688
13689 // private
13690 // hash to map config positions to true positions
13691 Roo.Resizable.positions = {
13692     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13693     hd: "hdrag"
13694 };
13695
13696 // private
13697 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13698     if(!this.tpl){
13699         // only initialize the template if resizable is used
13700         var tpl = Roo.DomHelper.createTemplate(
13701             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13702         );
13703         tpl.compile();
13704         Roo.Resizable.Handle.prototype.tpl = tpl;
13705     }
13706     this.position = pos;
13707     this.rz = rz;
13708     // show north drag fro topdra
13709     var handlepos = pos == 'hdrag' ? 'north' : pos;
13710     
13711     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13712     if (pos == 'hdrag') {
13713         this.el.setStyle('cursor', 'pointer');
13714     }
13715     this.el.unselectable();
13716     if(transparent){
13717         this.el.setOpacity(0);
13718     }
13719     this.el.on("mousedown", this.onMouseDown, this);
13720     if(!disableTrackOver){
13721         this.el.on("mouseover", this.onMouseOver, this);
13722         this.el.on("mouseout", this.onMouseOut, this);
13723     }
13724 };
13725
13726 // private
13727 Roo.Resizable.Handle.prototype = {
13728     afterResize : function(rz){
13729         // do nothing
13730     },
13731     // private
13732     onMouseDown : function(e){
13733         this.rz.onMouseDown(this, e);
13734     },
13735     // private
13736     onMouseOver : function(e){
13737         this.rz.handleOver(this, e);
13738     },
13739     // private
13740     onMouseOut : function(e){
13741         this.rz.handleOut(this, e);
13742     }
13743 };