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  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
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     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
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      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <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,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * 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
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir"
4724     };
4725
4726     if(config && config.data){
4727         this.inlineData = config.data;
4728         delete config.data;
4729     }
4730
4731     Roo.apply(this, config);
4732     
4733     if(this.reader){ // reader passed
4734         this.reader = Roo.factory(this.reader, Roo.data);
4735         this.reader.xmodule = this.xmodule || false;
4736         if(!this.recordType){
4737             this.recordType = this.reader.recordType;
4738         }
4739         if(this.reader.onMetaChange){
4740             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4741         }
4742     }
4743
4744     if(this.recordType){
4745         this.fields = this.recordType.prototype.fields;
4746     }
4747     this.modified = [];
4748
4749     this.addEvents({
4750         /**
4751          * @event datachanged
4752          * Fires when the data cache has changed, and a widget which is using this Store
4753          * as a Record cache should refresh its view.
4754          * @param {Store} this
4755          */
4756         datachanged : true,
4757         /**
4758          * @event metachange
4759          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4760          * @param {Store} this
4761          * @param {Object} meta The JSON metadata
4762          */
4763         metachange : true,
4764         /**
4765          * @event add
4766          * Fires when Records have been added to the Store
4767          * @param {Store} this
4768          * @param {Roo.data.Record[]} records The array of Records added
4769          * @param {Number} index The index at which the record(s) were added
4770          */
4771         add : true,
4772         /**
4773          * @event remove
4774          * Fires when a Record has been removed from the Store
4775          * @param {Store} this
4776          * @param {Roo.data.Record} record The Record that was removed
4777          * @param {Number} index The index at which the record was removed
4778          */
4779         remove : true,
4780         /**
4781          * @event update
4782          * Fires when a Record has been updated
4783          * @param {Store} this
4784          * @param {Roo.data.Record} record The Record that was updated
4785          * @param {String} operation The update operation being performed.  Value may be one of:
4786          * <pre><code>
4787  Roo.data.Record.EDIT
4788  Roo.data.Record.REJECT
4789  Roo.data.Record.COMMIT
4790          * </code></pre>
4791          */
4792         update : true,
4793         /**
4794          * @event clear
4795          * Fires when the data cache has been cleared.
4796          * @param {Store} this
4797          */
4798         clear : true,
4799         /**
4800          * @event beforeload
4801          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4802          * the load action will be canceled.
4803          * @param {Store} this
4804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4805          */
4806         beforeload : true,
4807         /**
4808          * @event load
4809          * Fires after a new set of Records has been loaded.
4810          * @param {Store} this
4811          * @param {Roo.data.Record[]} records The Records that were loaded
4812          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4813          */
4814         load : true,
4815         /**
4816          * @event loadexception
4817          * Fires if an exception occurs in the Proxy during loading.
4818          * Called with the signature of the Proxy's "loadexception" event.
4819          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4820          * 
4821          * @param {Proxy} 
4822          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4823          * @param {Object} load options 
4824          * @param {Object} jsonData from your request (normally this contains the Exception)
4825          */
4826         loadexception : true
4827     });
4828     
4829     if(this.proxy){
4830         this.proxy = Roo.factory(this.proxy, Roo.data);
4831         this.proxy.xmodule = this.xmodule || false;
4832         this.relayEvents(this.proxy,  ["loadexception"]);
4833     }
4834     this.sortToggle = {};
4835
4836     Roo.data.Store.superclass.constructor.call(this);
4837
4838     if(this.inlineData){
4839         this.loadData(this.inlineData);
4840         delete this.inlineData;
4841     }
4842 };
4843 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4844      /**
4845     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4846     * without a remote query - used by combo/forms at present.
4847     */
4848     
4849     /**
4850     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4851     */
4852     /**
4853     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4854     */
4855     /**
4856     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4857     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4858     */
4859     /**
4860     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4861     * on any HTTP request
4862     */
4863     /**
4864     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4865     */
4866     /**
4867     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4868     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4869     */
4870     remoteSort : false,
4871
4872     /**
4873     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4874      * loaded or when a record is removed. (defaults to false).
4875     */
4876     pruneModifiedRecords : false,
4877
4878     // private
4879     lastOptions : null,
4880
4881     /**
4882      * Add Records to the Store and fires the add event.
4883      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4884      */
4885     add : function(records){
4886         records = [].concat(records);
4887         for(var i = 0, len = records.length; i < len; i++){
4888             records[i].join(this);
4889         }
4890         var index = this.data.length;
4891         this.data.addAll(records);
4892         this.fireEvent("add", this, records, index);
4893     },
4894
4895     /**
4896      * Remove a Record from the Store and fires the remove event.
4897      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4898      */
4899     remove : function(record){
4900         var index = this.data.indexOf(record);
4901         this.data.removeAt(index);
4902         if(this.pruneModifiedRecords){
4903             this.modified.remove(record);
4904         }
4905         this.fireEvent("remove", this, record, index);
4906     },
4907
4908     /**
4909      * Remove all Records from the Store and fires the clear event.
4910      */
4911     removeAll : function(){
4912         this.data.clear();
4913         if(this.pruneModifiedRecords){
4914             this.modified = [];
4915         }
4916         this.fireEvent("clear", this);
4917     },
4918
4919     /**
4920      * Inserts Records to the Store at the given index and fires the add event.
4921      * @param {Number} index The start index at which to insert the passed Records.
4922      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4923      */
4924     insert : function(index, records){
4925         records = [].concat(records);
4926         for(var i = 0, len = records.length; i < len; i++){
4927             this.data.insert(index, records[i]);
4928             records[i].join(this);
4929         }
4930         this.fireEvent("add", this, records, index);
4931     },
4932
4933     /**
4934      * Get the index within the cache of the passed Record.
4935      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4936      * @return {Number} The index of the passed Record. Returns -1 if not found.
4937      */
4938     indexOf : function(record){
4939         return this.data.indexOf(record);
4940     },
4941
4942     /**
4943      * Get the index within the cache of the Record with the passed id.
4944      * @param {String} id The id of the Record to find.
4945      * @return {Number} The index of the Record. Returns -1 if not found.
4946      */
4947     indexOfId : function(id){
4948         return this.data.indexOfKey(id);
4949     },
4950
4951     /**
4952      * Get the Record with the specified id.
4953      * @param {String} id The id of the Record to find.
4954      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4955      */
4956     getById : function(id){
4957         return this.data.key(id);
4958     },
4959
4960     /**
4961      * Get the Record at the specified index.
4962      * @param {Number} index The index of the Record to find.
4963      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4964      */
4965     getAt : function(index){
4966         return this.data.itemAt(index);
4967     },
4968
4969     /**
4970      * Returns a range of Records between specified indices.
4971      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4972      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4973      * @return {Roo.data.Record[]} An array of Records
4974      */
4975     getRange : function(start, end){
4976         return this.data.getRange(start, end);
4977     },
4978
4979     // private
4980     storeOptions : function(o){
4981         o = Roo.apply({}, o);
4982         delete o.callback;
4983         delete o.scope;
4984         this.lastOptions = o;
4985     },
4986
4987     /**
4988      * Loads the Record cache from the configured Proxy using the configured Reader.
4989      * <p>
4990      * If using remote paging, then the first load call must specify the <em>start</em>
4991      * and <em>limit</em> properties in the options.params property to establish the initial
4992      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4993      * <p>
4994      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4995      * and this call will return before the new data has been loaded. Perform any post-processing
4996      * in a callback function, or in a "load" event handler.</strong>
4997      * <p>
4998      * @param {Object} options An object containing properties which control loading options:<ul>
4999      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5000      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5001      * passed the following arguments:<ul>
5002      * <li>r : Roo.data.Record[]</li>
5003      * <li>options: Options object from the load call</li>
5004      * <li>success: Boolean success indicator</li></ul></li>
5005      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5006      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5007      * </ul>
5008      */
5009     load : function(options){
5010         options = options || {};
5011         if(this.fireEvent("beforeload", this, options) !== false){
5012             this.storeOptions(options);
5013             var p = Roo.apply(options.params || {}, this.baseParams);
5014             // if meta was not loaded from remote source.. try requesting it.
5015             if (!this.reader.metaFromRemote) {
5016                 p._requestMeta = 1;
5017             }
5018             if(this.sortInfo && this.remoteSort){
5019                 var pn = this.paramNames;
5020                 p[pn["sort"]] = this.sortInfo.field;
5021                 p[pn["dir"]] = this.sortInfo.direction;
5022             }
5023             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5024         }
5025     },
5026
5027     /**
5028      * Reloads the Record cache from the configured Proxy using the configured Reader and
5029      * the options from the last load operation performed.
5030      * @param {Object} options (optional) An object containing properties which may override the options
5031      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5032      * the most recently used options are reused).
5033      */
5034     reload : function(options){
5035         this.load(Roo.applyIf(options||{}, this.lastOptions));
5036     },
5037
5038     // private
5039     // Called as a callback by the Reader during a load operation.
5040     loadRecords : function(o, options, success){
5041         if(!o || success === false){
5042             if(success !== false){
5043                 this.fireEvent("load", this, [], options);
5044             }
5045             if(options.callback){
5046                 options.callback.call(options.scope || this, [], options, false);
5047             }
5048             return;
5049         }
5050         // if data returned failure - throw an exception.
5051         if (o.success === false) {
5052             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5053             return;
5054         }
5055         var r = o.records, t = o.totalRecords || r.length;
5056         if(!options || options.add !== true){
5057             if(this.pruneModifiedRecords){
5058                 this.modified = [];
5059             }
5060             for(var i = 0, len = r.length; i < len; i++){
5061                 r[i].join(this);
5062             }
5063             if(this.snapshot){
5064                 this.data = this.snapshot;
5065                 delete this.snapshot;
5066             }
5067             this.data.clear();
5068             this.data.addAll(r);
5069             this.totalLength = t;
5070             this.applySort();
5071             this.fireEvent("datachanged", this);
5072         }else{
5073             this.totalLength = Math.max(t, this.data.length+r.length);
5074             this.add(r);
5075         }
5076         this.fireEvent("load", this, r, options);
5077         if(options.callback){
5078             options.callback.call(options.scope || this, r, options, true);
5079         }
5080     },
5081
5082     /**
5083      * Loads data from a passed data block. A Reader which understands the format of the data
5084      * must have been configured in the constructor.
5085      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5086      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5087      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5088      */
5089     loadData : function(o, append){
5090         var r = this.reader.readRecords(o);
5091         this.loadRecords(r, {add: append}, true);
5092     },
5093
5094     /**
5095      * Gets the number of cached records.
5096      * <p>
5097      * <em>If using paging, this may not be the total size of the dataset. If the data object
5098      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5099      * the data set size</em>
5100      */
5101     getCount : function(){
5102         return this.data.length || 0;
5103     },
5104
5105     /**
5106      * Gets the total number of records in the dataset as returned by the server.
5107      * <p>
5108      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5109      * the dataset size</em>
5110      */
5111     getTotalCount : function(){
5112         return this.totalLength || 0;
5113     },
5114
5115     /**
5116      * Returns the sort state of the Store as an object with two properties:
5117      * <pre><code>
5118  field {String} The name of the field by which the Records are sorted
5119  direction {String} The sort order, "ASC" or "DESC"
5120      * </code></pre>
5121      */
5122     getSortState : function(){
5123         return this.sortInfo;
5124     },
5125
5126     // private
5127     applySort : function(){
5128         if(this.sortInfo && !this.remoteSort){
5129             var s = this.sortInfo, f = s.field;
5130             var st = this.fields.get(f).sortType;
5131             var fn = function(r1, r2){
5132                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5133                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5134             };
5135             this.data.sort(s.direction, fn);
5136             if(this.snapshot && this.snapshot != this.data){
5137                 this.snapshot.sort(s.direction, fn);
5138             }
5139         }
5140     },
5141
5142     /**
5143      * Sets the default sort column and order to be used by the next load operation.
5144      * @param {String} fieldName The name of the field to sort by.
5145      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5146      */
5147     setDefaultSort : function(field, dir){
5148         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5149     },
5150
5151     /**
5152      * Sort the Records.
5153      * If remote sorting is used, the sort is performed on the server, and the cache is
5154      * reloaded. If local sorting is used, the cache is sorted internally.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     sort : function(fieldName, dir){
5159         var f = this.fields.get(fieldName);
5160         if(!dir){
5161             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5162                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5163             }else{
5164                 dir = f.sortDir;
5165             }
5166         }
5167         this.sortToggle[f.name] = dir;
5168         this.sortInfo = {field: f.name, direction: dir};
5169         if(!this.remoteSort){
5170             this.applySort();
5171             this.fireEvent("datachanged", this);
5172         }else{
5173             this.load(this.lastOptions);
5174         }
5175     },
5176
5177     /**
5178      * Calls the specified function for each of the Records in the cache.
5179      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5180      * Returning <em>false</em> aborts and exits the iteration.
5181      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5182      */
5183     each : function(fn, scope){
5184         this.data.each(fn, scope);
5185     },
5186
5187     /**
5188      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5189      * (e.g., during paging).
5190      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5191      */
5192     getModifiedRecords : function(){
5193         return this.modified;
5194     },
5195
5196     // private
5197     createFilterFn : function(property, value, anyMatch){
5198         if(!value.exec){ // not a regex
5199             value = String(value);
5200             if(value.length == 0){
5201                 return false;
5202             }
5203             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5204         }
5205         return function(r){
5206             return value.test(r.data[property]);
5207         };
5208     },
5209
5210     /**
5211      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5212      * @param {String} property A field on your records
5213      * @param {Number} start The record index to start at (defaults to 0)
5214      * @param {Number} end The last record index to include (defaults to length - 1)
5215      * @return {Number} The sum
5216      */
5217     sum : function(property, start, end){
5218         var rs = this.data.items, v = 0;
5219         start = start || 0;
5220         end = (end || end === 0) ? end : rs.length-1;
5221
5222         for(var i = start; i <= end; i++){
5223             v += (rs[i].data[property] || 0);
5224         }
5225         return v;
5226     },
5227
5228     /**
5229      * Filter the records by a specified property.
5230      * @param {String} field A field on your records
5231      * @param {String/RegExp} value Either a string that the field
5232      * should start with or a RegExp to test against the field
5233      * @param {Boolean} anyMatch True to match any part not just the beginning
5234      */
5235     filter : function(property, value, anyMatch){
5236         var fn = this.createFilterFn(property, value, anyMatch);
5237         return fn ? this.filterBy(fn) : this.clearFilter();
5238     },
5239
5240     /**
5241      * Filter by a function. The specified function will be called with each
5242      * record in this data source. If the function returns true the record is included,
5243      * otherwise it is filtered.
5244      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5245      * @param {Object} scope (optional) The scope of the function (defaults to this)
5246      */
5247     filterBy : function(fn, scope){
5248         this.snapshot = this.snapshot || this.data;
5249         this.data = this.queryBy(fn, scope||this);
5250         this.fireEvent("datachanged", this);
5251     },
5252
5253     /**
5254      * Query the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5260      */
5261     query : function(property, value, anyMatch){
5262         var fn = this.createFilterFn(property, value, anyMatch);
5263         return fn ? this.queryBy(fn) : this.data.clone();
5264     },
5265
5266     /**
5267      * Query by a function. The specified function will be called with each
5268      * record in this data source. If the function returns true the record is included
5269      * in the results.
5270      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5271      * @param {Object} scope (optional) The scope of the function (defaults to this)
5272       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      **/
5274     queryBy : function(fn, scope){
5275         var data = this.snapshot || this.data;
5276         return data.filterBy(fn, scope||this);
5277     },
5278
5279     /**
5280      * Collects unique values for a particular dataIndex from this store.
5281      * @param {String} dataIndex The property to collect
5282      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5283      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5284      * @return {Array} An array of the unique values
5285      **/
5286     collect : function(dataIndex, allowNull, bypassFilter){
5287         var d = (bypassFilter === true && this.snapshot) ?
5288                 this.snapshot.items : this.data.items;
5289         var v, sv, r = [], l = {};
5290         for(var i = 0, len = d.length; i < len; i++){
5291             v = d[i].data[dataIndex];
5292             sv = String(v);
5293             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5294                 l[sv] = true;
5295                 r[r.length] = v;
5296             }
5297         }
5298         return r;
5299     },
5300
5301     /**
5302      * Revert to a view of the Record cache with no filtering applied.
5303      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5304      */
5305     clearFilter : function(suppressEvent){
5306         if(this.snapshot && this.snapshot != this.data){
5307             this.data = this.snapshot;
5308             delete this.snapshot;
5309             if(suppressEvent !== true){
5310                 this.fireEvent("datachanged", this);
5311             }
5312         }
5313     },
5314
5315     // private
5316     afterEdit : function(record){
5317         if(this.modified.indexOf(record) == -1){
5318             this.modified.push(record);
5319         }
5320         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5321     },
5322
5323     // private
5324     afterReject : function(record){
5325         this.modified.remove(record);
5326         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5327     },
5328
5329     // private
5330     afterCommit : function(record){
5331         this.modified.remove(record);
5332         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5333     },
5334
5335     /**
5336      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5337      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5338      */
5339     commitChanges : function(){
5340         var m = this.modified.slice(0);
5341         this.modified = [];
5342         for(var i = 0, len = m.length; i < len; i++){
5343             m[i].commit();
5344         }
5345     },
5346
5347     /**
5348      * Cancel outstanding changes on all changed records.
5349      */
5350     rejectChanges : function(){
5351         var m = this.modified.slice(0);
5352         this.modified = [];
5353         for(var i = 0, len = m.length; i < len; i++){
5354             m[i].reject();
5355         }
5356     },
5357
5358     onMetaChange : function(meta, rtype, o){
5359         this.recordType = rtype;
5360         this.fields = rtype.prototype.fields;
5361         delete this.snapshot;
5362         this.sortInfo = meta.sortInfo || this.sortInfo;
5363         this.modified = [];
5364         this.fireEvent('metachange', this, this.reader.meta);
5365     }
5366 });/*
5367  * Based on:
5368  * Ext JS Library 1.1.1
5369  * Copyright(c) 2006-2007, Ext JS, LLC.
5370  *
5371  * Originally Released Under LGPL - original licence link has changed is not relivant.
5372  *
5373  * Fork - LGPL
5374  * <script type="text/javascript">
5375  */
5376
5377 /**
5378  * @class Roo.data.SimpleStore
5379  * @extends Roo.data.Store
5380  * Small helper class to make creating Stores from Array data easier.
5381  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5382  * @cfg {Array} fields An array of field definition objects, or field name strings.
5383  * @cfg {Array} data The multi-dimensional array of data
5384  * @constructor
5385  * @param {Object} config
5386  */
5387 Roo.data.SimpleStore = function(config){
5388     Roo.data.SimpleStore.superclass.constructor.call(this, {
5389         isLocal : true,
5390         reader: new Roo.data.ArrayReader({
5391                 id: config.id
5392             },
5393             Roo.data.Record.create(config.fields)
5394         ),
5395         proxy : new Roo.data.MemoryProxy(config.data)
5396     });
5397     this.load();
5398 };
5399 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409
5410 /**
5411 /**
5412  * @extends Roo.data.Store
5413  * @class Roo.data.JsonStore
5414  * Small helper class to make creating Stores for JSON data easier. <br/>
5415 <pre><code>
5416 var store = new Roo.data.JsonStore({
5417     url: 'get-images.php',
5418     root: 'images',
5419     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5420 });
5421 </code></pre>
5422  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5423  * JsonReader and HttpProxy (unless inline data is provided).</b>
5424  * @cfg {Array} fields An array of field definition objects, or field name strings.
5425  * @constructor
5426  * @param {Object} config
5427  */
5428 Roo.data.JsonStore = function(c){
5429     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5430         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5431         reader: new Roo.data.JsonReader(c, c.fields)
5432     }));
5433 };
5434 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5435  * Based on:
5436  * Ext JS Library 1.1.1
5437  * Copyright(c) 2006-2007, Ext JS, LLC.
5438  *
5439  * Originally Released Under LGPL - original licence link has changed is not relivant.
5440  *
5441  * Fork - LGPL
5442  * <script type="text/javascript">
5443  */
5444
5445  
5446 Roo.data.Field = function(config){
5447     if(typeof config == "string"){
5448         config = {name: config};
5449     }
5450     Roo.apply(this, config);
5451     
5452     if(!this.type){
5453         this.type = "auto";
5454     }
5455     
5456     var st = Roo.data.SortTypes;
5457     // named sortTypes are supported, here we look them up
5458     if(typeof this.sortType == "string"){
5459         this.sortType = st[this.sortType];
5460     }
5461     
5462     // set default sortType for strings and dates
5463     if(!this.sortType){
5464         switch(this.type){
5465             case "string":
5466                 this.sortType = st.asUCString;
5467                 break;
5468             case "date":
5469                 this.sortType = st.asDate;
5470                 break;
5471             default:
5472                 this.sortType = st.none;
5473         }
5474     }
5475
5476     // define once
5477     var stripRe = /[\$,%]/g;
5478
5479     // prebuilt conversion function for this field, instead of
5480     // switching every time we're reading a value
5481     if(!this.convert){
5482         var cv, dateFormat = this.dateFormat;
5483         switch(this.type){
5484             case "":
5485             case "auto":
5486             case undefined:
5487                 cv = function(v){ return v; };
5488                 break;
5489             case "string":
5490                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5491                 break;
5492             case "int":
5493                 cv = function(v){
5494                     return v !== undefined && v !== null && v !== '' ?
5495                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5496                     };
5497                 break;
5498             case "float":
5499                 cv = function(v){
5500                     return v !== undefined && v !== null && v !== '' ?
5501                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5502                     };
5503                 break;
5504             case "bool":
5505             case "boolean":
5506                 cv = function(v){ return v === true || v === "true" || v == 1; };
5507                 break;
5508             case "date":
5509                 cv = function(v){
5510                     if(!v){
5511                         return '';
5512                     }
5513                     if(v instanceof Date){
5514                         return v;
5515                     }
5516                     if(dateFormat){
5517                         if(dateFormat == "timestamp"){
5518                             return new Date(v*1000);
5519                         }
5520                         return Date.parseDate(v, dateFormat);
5521                     }
5522                     var parsed = Date.parse(v);
5523                     return parsed ? new Date(parsed) : null;
5524                 };
5525              break;
5526             
5527         }
5528         this.convert = cv;
5529     }
5530 };
5531
5532 Roo.data.Field.prototype = {
5533     dateFormat: null,
5534     defaultValue: "",
5535     mapping: null,
5536     sortType : null,
5537     sortDir : "ASC"
5538 };/*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 // Base class for reading structured data from a data source.  This class is intended to be
5550 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5551
5552 /**
5553  * @class Roo.data.DataReader
5554  * Base class for reading structured data from a data source.  This class is intended to be
5555  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5556  */
5557
5558 Roo.data.DataReader = function(meta, recordType){
5559     
5560     this.meta = meta;
5561     
5562     this.recordType = recordType instanceof Array ? 
5563         Roo.data.Record.create(recordType) : recordType;
5564 };
5565
5566 Roo.data.DataReader.prototype = {
5567      /**
5568      * Create an empty record
5569      * @param {Object} data (optional) - overlay some values
5570      * @return {Roo.data.Record} record created.
5571      */
5572     newRow :  function(d) {
5573         var da =  {};
5574         this.recordType.prototype.fields.each(function(c) {
5575             switch( c.type) {
5576                 case 'int' : da[c.name] = 0; break;
5577                 case 'date' : da[c.name] = new Date(); break;
5578                 case 'float' : da[c.name] = 0.0; break;
5579                 case 'boolean' : da[c.name] = false; break;
5580                 default : da[c.name] = ""; break;
5581             }
5582             
5583         });
5584         return new this.recordType(Roo.apply(da, d));
5585     }
5586     
5587 };/*
5588  * Based on:
5589  * Ext JS Library 1.1.1
5590  * Copyright(c) 2006-2007, Ext JS, LLC.
5591  *
5592  * Originally Released Under LGPL - original licence link has changed is not relivant.
5593  *
5594  * Fork - LGPL
5595  * <script type="text/javascript">
5596  */
5597
5598 /**
5599  * @class Roo.data.DataProxy
5600  * @extends Roo.data.Observable
5601  * This class is an abstract base class for implementations which provide retrieval of
5602  * unformatted data objects.<br>
5603  * <p>
5604  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5605  * (of the appropriate type which knows how to parse the data object) to provide a block of
5606  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5607  * <p>
5608  * Custom implementations must implement the load method as described in
5609  * {@link Roo.data.HttpProxy#load}.
5610  */
5611 Roo.data.DataProxy = function(){
5612     this.addEvents({
5613         /**
5614          * @event beforeload
5615          * Fires before a network request is made to retrieve a data object.
5616          * @param {Object} This DataProxy object.
5617          * @param {Object} params The params parameter to the load function.
5618          */
5619         beforeload : true,
5620         /**
5621          * @event load
5622          * Fires before the load method's callback is called.
5623          * @param {Object} This DataProxy object.
5624          * @param {Object} o The data object.
5625          * @param {Object} arg The callback argument object passed to the load function.
5626          */
5627         load : true,
5628         /**
5629          * @event loadexception
5630          * Fires if an Exception occurs during data retrieval.
5631          * @param {Object} This DataProxy object.
5632          * @param {Object} o The data object.
5633          * @param {Object} arg The callback argument object passed to the load function.
5634          * @param {Object} e The Exception.
5635          */
5636         loadexception : true
5637     });
5638     Roo.data.DataProxy.superclass.constructor.call(this);
5639 };
5640
5641 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5642
5643     /**
5644      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5645      */
5646 /*
5647  * Based on:
5648  * Ext JS Library 1.1.1
5649  * Copyright(c) 2006-2007, Ext JS, LLC.
5650  *
5651  * Originally Released Under LGPL - original licence link has changed is not relivant.
5652  *
5653  * Fork - LGPL
5654  * <script type="text/javascript">
5655  */
5656 /**
5657  * @class Roo.data.MemoryProxy
5658  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5659  * to the Reader when its load method is called.
5660  * @constructor
5661  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5662  */
5663 Roo.data.MemoryProxy = function(data){
5664     if (data.data) {
5665         data = data.data;
5666     }
5667     Roo.data.MemoryProxy.superclass.constructor.call(this);
5668     this.data = data;
5669 };
5670
5671 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5672     /**
5673      * Load data from the requested source (in this case an in-memory
5674      * data object passed to the constructor), read the data object into
5675      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5676      * process that block using the passed callback.
5677      * @param {Object} params This parameter is not used by the MemoryProxy class.
5678      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5679      * object into a block of Roo.data.Records.
5680      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5681      * The function must be passed <ul>
5682      * <li>The Record block object</li>
5683      * <li>The "arg" argument from the load function</li>
5684      * <li>A boolean success indicator</li>
5685      * </ul>
5686      * @param {Object} scope The scope in which to call the callback
5687      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5688      */
5689     load : function(params, reader, callback, scope, arg){
5690         params = params || {};
5691         var result;
5692         try {
5693             result = reader.readRecords(this.data);
5694         }catch(e){
5695             this.fireEvent("loadexception", this, arg, null, e);
5696             callback.call(scope, null, arg, false);
5697             return;
5698         }
5699         callback.call(scope, result, arg, true);
5700     },
5701     
5702     // private
5703     update : function(params, records){
5704         
5705     }
5706 });/*
5707  * Based on:
5708  * Ext JS Library 1.1.1
5709  * Copyright(c) 2006-2007, Ext JS, LLC.
5710  *
5711  * Originally Released Under LGPL - original licence link has changed is not relivant.
5712  *
5713  * Fork - LGPL
5714  * <script type="text/javascript">
5715  */
5716 /**
5717  * @class Roo.data.HttpProxy
5718  * @extends Roo.data.DataProxy
5719  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5720  * configured to reference a certain URL.<br><br>
5721  * <p>
5722  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5723  * from which the running page was served.<br><br>
5724  * <p>
5725  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5726  * <p>
5727  * Be aware that to enable the browser to parse an XML document, the server must set
5728  * the Content-Type header in the HTTP response to "text/xml".
5729  * @constructor
5730  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5731  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5732  * will be used to make the request.
5733  */
5734 Roo.data.HttpProxy = function(conn){
5735     Roo.data.HttpProxy.superclass.constructor.call(this);
5736     // is conn a conn config or a real conn?
5737     this.conn = conn;
5738     this.useAjax = !conn || !conn.events;
5739   
5740 };
5741
5742 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5743     // thse are take from connection...
5744     
5745     /**
5746      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5747      */
5748     /**
5749      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5750      * extra parameters to each request made by this object. (defaults to undefined)
5751      */
5752     /**
5753      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5754      *  to each request made by this object. (defaults to undefined)
5755      */
5756     /**
5757      * @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)
5758      */
5759     /**
5760      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5761      */
5762      /**
5763      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5764      * @type Boolean
5765      */
5766   
5767
5768     /**
5769      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5770      * @type Boolean
5771      */
5772     /**
5773      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5774      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5775      * a finer-grained basis than the DataProxy events.
5776      */
5777     getConnection : function(){
5778         return this.useAjax ? Roo.Ajax : this.conn;
5779     },
5780
5781     /**
5782      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5783      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5784      * process that block using the passed callback.
5785      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5786      * for the request to the remote server.
5787      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5788      * object into a block of Roo.data.Records.
5789      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5790      * The function must be passed <ul>
5791      * <li>The Record block object</li>
5792      * <li>The "arg" argument from the load function</li>
5793      * <li>A boolean success indicator</li>
5794      * </ul>
5795      * @param {Object} scope The scope in which to call the callback
5796      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5797      */
5798     load : function(params, reader, callback, scope, arg){
5799         if(this.fireEvent("beforeload", this, params) !== false){
5800             var  o = {
5801                 params : params || {},
5802                 request: {
5803                     callback : callback,
5804                     scope : scope,
5805                     arg : arg
5806                 },
5807                 reader: reader,
5808                 callback : this.loadResponse,
5809                 scope: this
5810             };
5811             if(this.useAjax){
5812                 Roo.applyIf(o, this.conn);
5813                 if(this.activeRequest){
5814                     Roo.Ajax.abort(this.activeRequest);
5815                 }
5816                 this.activeRequest = Roo.Ajax.request(o);
5817             }else{
5818                 this.conn.request(o);
5819             }
5820         }else{
5821             callback.call(scope||this, null, arg, false);
5822         }
5823     },
5824
5825     // private
5826     loadResponse : function(o, success, response){
5827         delete this.activeRequest;
5828         if(!success){
5829             this.fireEvent("loadexception", this, o, response);
5830             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5831             return;
5832         }
5833         var result;
5834         try {
5835             result = o.reader.read(response);
5836         }catch(e){
5837             this.fireEvent("loadexception", this, o, response, e);
5838             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5839             return;
5840         }
5841         
5842         this.fireEvent("load", this, o, o.request.arg);
5843         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5844     },
5845
5846     // private
5847     update : function(dataSet){
5848
5849     },
5850
5851     // private
5852     updateResponse : function(dataSet){
5853
5854     }
5855 });/*
5856  * Based on:
5857  * Ext JS Library 1.1.1
5858  * Copyright(c) 2006-2007, Ext JS, LLC.
5859  *
5860  * Originally Released Under LGPL - original licence link has changed is not relivant.
5861  *
5862  * Fork - LGPL
5863  * <script type="text/javascript">
5864  */
5865
5866 /**
5867  * @class Roo.data.ScriptTagProxy
5868  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5869  * other than the originating domain of the running page.<br><br>
5870  * <p>
5871  * <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
5872  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5873  * <p>
5874  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5875  * source code that is used as the source inside a &lt;script> tag.<br><br>
5876  * <p>
5877  * In order for the browser to process the returned data, the server must wrap the data object
5878  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5879  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5880  * depending on whether the callback name was passed:
5881  * <p>
5882  * <pre><code>
5883 boolean scriptTag = false;
5884 String cb = request.getParameter("callback");
5885 if (cb != null) {
5886     scriptTag = true;
5887     response.setContentType("text/javascript");
5888 } else {
5889     response.setContentType("application/x-json");
5890 }
5891 Writer out = response.getWriter();
5892 if (scriptTag) {
5893     out.write(cb + "(");
5894 }
5895 out.print(dataBlock.toJsonString());
5896 if (scriptTag) {
5897     out.write(");");
5898 }
5899 </pre></code>
5900  *
5901  * @constructor
5902  * @param {Object} config A configuration object.
5903  */
5904 Roo.data.ScriptTagProxy = function(config){
5905     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5906     Roo.apply(this, config);
5907     this.head = document.getElementsByTagName("head")[0];
5908 };
5909
5910 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5911
5912 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5913     /**
5914      * @cfg {String} url The URL from which to request the data object.
5915      */
5916     /**
5917      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5918      */
5919     timeout : 30000,
5920     /**
5921      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5922      * the server the name of the callback function set up by the load call to process the returned data object.
5923      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5924      * javascript output which calls this named function passing the data object as its only parameter.
5925      */
5926     callbackParam : "callback",
5927     /**
5928      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5929      * name to the request.
5930      */
5931     nocache : true,
5932
5933     /**
5934      * Load data from the configured URL, read the data object into
5935      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5936      * process that block using the passed callback.
5937      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5938      * for the request to the remote server.
5939      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5940      * object into a block of Roo.data.Records.
5941      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5942      * The function must be passed <ul>
5943      * <li>The Record block object</li>
5944      * <li>The "arg" argument from the load function</li>
5945      * <li>A boolean success indicator</li>
5946      * </ul>
5947      * @param {Object} scope The scope in which to call the callback
5948      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5949      */
5950     load : function(params, reader, callback, scope, arg){
5951         if(this.fireEvent("beforeload", this, params) !== false){
5952
5953             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5954
5955             var url = this.url;
5956             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5957             if(this.nocache){
5958                 url += "&_dc=" + (new Date().getTime());
5959             }
5960             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5961             var trans = {
5962                 id : transId,
5963                 cb : "stcCallback"+transId,
5964                 scriptId : "stcScript"+transId,
5965                 params : params,
5966                 arg : arg,
5967                 url : url,
5968                 callback : callback,
5969                 scope : scope,
5970                 reader : reader
5971             };
5972             var conn = this;
5973
5974             window[trans.cb] = function(o){
5975                 conn.handleResponse(o, trans);
5976             };
5977
5978             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5979
5980             if(this.autoAbort !== false){
5981                 this.abort();
5982             }
5983
5984             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5985
5986             var script = document.createElement("script");
5987             script.setAttribute("src", url);
5988             script.setAttribute("type", "text/javascript");
5989             script.setAttribute("id", trans.scriptId);
5990             this.head.appendChild(script);
5991
5992             this.trans = trans;
5993         }else{
5994             callback.call(scope||this, null, arg, false);
5995         }
5996     },
5997
5998     // private
5999     isLoading : function(){
6000         return this.trans ? true : false;
6001     },
6002
6003     /**
6004      * Abort the current server request.
6005      */
6006     abort : function(){
6007         if(this.isLoading()){
6008             this.destroyTrans(this.trans);
6009         }
6010     },
6011
6012     // private
6013     destroyTrans : function(trans, isLoaded){
6014         this.head.removeChild(document.getElementById(trans.scriptId));
6015         clearTimeout(trans.timeoutId);
6016         if(isLoaded){
6017             window[trans.cb] = undefined;
6018             try{
6019                 delete window[trans.cb];
6020             }catch(e){}
6021         }else{
6022             // if hasn't been loaded, wait for load to remove it to prevent script error
6023             window[trans.cb] = function(){
6024                 window[trans.cb] = undefined;
6025                 try{
6026                     delete window[trans.cb];
6027                 }catch(e){}
6028             };
6029         }
6030     },
6031
6032     // private
6033     handleResponse : function(o, trans){
6034         this.trans = false;
6035         this.destroyTrans(trans, true);
6036         var result;
6037         try {
6038             result = trans.reader.readRecords(o);
6039         }catch(e){
6040             this.fireEvent("loadexception", this, o, trans.arg, e);
6041             trans.callback.call(trans.scope||window, null, trans.arg, false);
6042             return;
6043         }
6044         this.fireEvent("load", this, o, trans.arg);
6045         trans.callback.call(trans.scope||window, result, trans.arg, true);
6046     },
6047
6048     // private
6049     handleFailure : function(trans){
6050         this.trans = false;
6051         this.destroyTrans(trans, false);
6052         this.fireEvent("loadexception", this, null, trans.arg);
6053         trans.callback.call(trans.scope||window, null, trans.arg, false);
6054     }
6055 });/*
6056  * Based on:
6057  * Ext JS Library 1.1.1
6058  * Copyright(c) 2006-2007, Ext JS, LLC.
6059  *
6060  * Originally Released Under LGPL - original licence link has changed is not relivant.
6061  *
6062  * Fork - LGPL
6063  * <script type="text/javascript">
6064  */
6065
6066 /**
6067  * @class Roo.data.JsonReader
6068  * @extends Roo.data.DataReader
6069  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6070  * based on mappings in a provided Roo.data.Record constructor.
6071  * 
6072  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6073  * in the reply previously. 
6074  * 
6075  * <p>
6076  * Example code:
6077  * <pre><code>
6078 var RecordDef = Roo.data.Record.create([
6079     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6080     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6081 ]);
6082 var myReader = new Roo.data.JsonReader({
6083     totalProperty: "results",    // The property which contains the total dataset size (optional)
6084     root: "rows",                // The property which contains an Array of row objects
6085     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6086 }, RecordDef);
6087 </code></pre>
6088  * <p>
6089  * This would consume a JSON file like this:
6090  * <pre><code>
6091 { 'results': 2, 'rows': [
6092     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6093     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6094 }
6095 </code></pre>
6096  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6097  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6098  * paged from the remote server.
6099  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6100  * @cfg {String} root name of the property which contains the Array of row objects.
6101  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6102  * @constructor
6103  * Create a new JsonReader
6104  * @param {Object} meta Metadata configuration options
6105  * @param {Object} recordType Either an Array of field definition objects,
6106  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6107  */
6108 Roo.data.JsonReader = function(meta, recordType){
6109     
6110     meta = meta || {};
6111     // set some defaults:
6112     Roo.applyIf(meta, {
6113         totalProperty: 'total',
6114         successProperty : 'success',
6115         root : 'data',
6116         id : 'id'
6117     });
6118     
6119     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6120 };
6121 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6122     
6123     /**
6124      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6125      * Used by Store query builder to append _requestMeta to params.
6126      * 
6127      */
6128     metaFromRemote : false,
6129     /**
6130      * This method is only used by a DataProxy which has retrieved data from a remote server.
6131      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6132      * @return {Object} data A data block which is used by an Roo.data.Store object as
6133      * a cache of Roo.data.Records.
6134      */
6135     read : function(response){
6136         var json = response.responseText;
6137        
6138         var o = /* eval:var:o */ eval("("+json+")");
6139         if(!o) {
6140             throw {message: "JsonReader.read: Json object not found"};
6141         }
6142         
6143         if(o.metaData){
6144             
6145             delete this.ef;
6146             this.metaFromRemote = true;
6147             this.meta = o.metaData;
6148             this.recordType = Roo.data.Record.create(o.metaData.fields);
6149             this.onMetaChange(this.meta, this.recordType, o);
6150         }
6151         return this.readRecords(o);
6152     },
6153
6154     // private function a store will implement
6155     onMetaChange : function(meta, recordType, o){
6156
6157     },
6158
6159     /**
6160          * @ignore
6161          */
6162     simpleAccess: function(obj, subsc) {
6163         return obj[subsc];
6164     },
6165
6166         /**
6167          * @ignore
6168          */
6169     getJsonAccessor: function(){
6170         var re = /[\[\.]/;
6171         return function(expr) {
6172             try {
6173                 return(re.test(expr))
6174                     ? new Function("obj", "return obj." + expr)
6175                     : function(obj){
6176                         return obj[expr];
6177                     };
6178             } catch(e){}
6179             return Roo.emptyFn;
6180         };
6181     }(),
6182
6183     /**
6184      * Create a data block containing Roo.data.Records from an XML document.
6185      * @param {Object} o An object which contains an Array of row objects in the property specified
6186      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6187      * which contains the total size of the dataset.
6188      * @return {Object} data A data block which is used by an Roo.data.Store object as
6189      * a cache of Roo.data.Records.
6190      */
6191     readRecords : function(o){
6192         /**
6193          * After any data loads, the raw JSON data is available for further custom processing.
6194          * @type Object
6195          */
6196         this.jsonData = o;
6197         var s = this.meta, Record = this.recordType,
6198             f = Record.prototype.fields, fi = f.items, fl = f.length;
6199
6200 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6201         if (!this.ef) {
6202             if(s.totalProperty) {
6203                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6204                 }
6205                 if(s.successProperty) {
6206                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6207                 }
6208                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6209                 if (s.id) {
6210                         var g = this.getJsonAccessor(s.id);
6211                         this.getId = function(rec) {
6212                                 var r = g(rec);
6213                                 return (r === undefined || r === "") ? null : r;
6214                         };
6215                 } else {
6216                         this.getId = function(){return null;};
6217                 }
6218             this.ef = [];
6219             for(var jj = 0; jj < fl; jj++){
6220                 f = fi[jj];
6221                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6222                 this.ef[jj] = this.getJsonAccessor(map);
6223             }
6224         }
6225
6226         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6227         if(s.totalProperty){
6228             var vt = parseInt(this.getTotal(o), 10);
6229             if(!isNaN(vt)){
6230                 totalRecords = vt;
6231             }
6232         }
6233         if(s.successProperty){
6234             var vs = this.getSuccess(o);
6235             if(vs === false || vs === 'false'){
6236                 success = false;
6237             }
6238         }
6239         var records = [];
6240             for(var i = 0; i < c; i++){
6241                     var n = root[i];
6242                 var values = {};
6243                 var id = this.getId(n);
6244                 for(var j = 0; j < fl; j++){
6245                     f = fi[j];
6246                 var v = this.ef[j](n);
6247                 if (!f.convert) {
6248                     Roo.log('missing convert for ' + f.name);
6249                     Roo.log(f);
6250                     continue;
6251                 }
6252                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6253                 }
6254                 var record = new Record(values, id);
6255                 record.json = n;
6256                 records[i] = record;
6257             }
6258             return {
6259                 success : success,
6260                 records : records,
6261                 totalRecords : totalRecords
6262             };
6263     }
6264 });/*
6265  * Based on:
6266  * Ext JS Library 1.1.1
6267  * Copyright(c) 2006-2007, Ext JS, LLC.
6268  *
6269  * Originally Released Under LGPL - original licence link has changed is not relivant.
6270  *
6271  * Fork - LGPL
6272  * <script type="text/javascript">
6273  */
6274
6275 /**
6276  * @class Roo.data.XmlReader
6277  * @extends Roo.data.DataReader
6278  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6279  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6280  * <p>
6281  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6282  * header in the HTTP response must be set to "text/xml".</em>
6283  * <p>
6284  * Example code:
6285  * <pre><code>
6286 var RecordDef = Roo.data.Record.create([
6287    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6288    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6289 ]);
6290 var myReader = new Roo.data.XmlReader({
6291    totalRecords: "results", // The element which contains the total dataset size (optional)
6292    record: "row",           // The repeated element which contains row information
6293    id: "id"                 // The element within the row that provides an ID for the record (optional)
6294 }, RecordDef);
6295 </code></pre>
6296  * <p>
6297  * This would consume an XML file like this:
6298  * <pre><code>
6299 &lt;?xml?>
6300 &lt;dataset>
6301  &lt;results>2&lt;/results>
6302  &lt;row>
6303    &lt;id>1&lt;/id>
6304    &lt;name>Bill&lt;/name>
6305    &lt;occupation>Gardener&lt;/occupation>
6306  &lt;/row>
6307  &lt;row>
6308    &lt;id>2&lt;/id>
6309    &lt;name>Ben&lt;/name>
6310    &lt;occupation>Horticulturalist&lt;/occupation>
6311  &lt;/row>
6312 &lt;/dataset>
6313 </code></pre>
6314  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6315  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6316  * paged from the remote server.
6317  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6318  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6319  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6320  * a record identifier value.
6321  * @constructor
6322  * Create a new XmlReader
6323  * @param {Object} meta Metadata configuration options
6324  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6325  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6326  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6327  */
6328 Roo.data.XmlReader = function(meta, recordType){
6329     meta = meta || {};
6330     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6331 };
6332 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6333     /**
6334      * This method is only used by a DataProxy which has retrieved data from a remote server.
6335          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6336          * to contain a method called 'responseXML' that returns an XML document object.
6337      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6338      * a cache of Roo.data.Records.
6339      */
6340     read : function(response){
6341         var doc = response.responseXML;
6342         if(!doc) {
6343             throw {message: "XmlReader.read: XML Document not available"};
6344         }
6345         return this.readRecords(doc);
6346     },
6347
6348     /**
6349      * Create a data block containing Roo.data.Records from an XML document.
6350          * @param {Object} doc A parsed XML document.
6351      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6352      * a cache of Roo.data.Records.
6353      */
6354     readRecords : function(doc){
6355         /**
6356          * After any data loads/reads, the raw XML Document is available for further custom processing.
6357          * @type XMLDocument
6358          */
6359         this.xmlData = doc;
6360         var root = doc.documentElement || doc;
6361         var q = Roo.DomQuery;
6362         var recordType = this.recordType, fields = recordType.prototype.fields;
6363         var sid = this.meta.id;
6364         var totalRecords = 0, success = true;
6365         if(this.meta.totalRecords){
6366             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6367         }
6368         
6369         if(this.meta.success){
6370             var sv = q.selectValue(this.meta.success, root, true);
6371             success = sv !== false && sv !== 'false';
6372         }
6373         var records = [];
6374         var ns = q.select(this.meta.record, root);
6375         for(var i = 0, len = ns.length; i < len; i++) {
6376                 var n = ns[i];
6377                 var values = {};
6378                 var id = sid ? q.selectValue(sid, n) : undefined;
6379                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6380                     var f = fields.items[j];
6381                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6382                     v = f.convert(v);
6383                     values[f.name] = v;
6384                 }
6385                 var record = new recordType(values, id);
6386                 record.node = n;
6387                 records[records.length] = record;
6388             }
6389
6390             return {
6391                 success : success,
6392                 records : records,
6393                 totalRecords : totalRecords || records.length
6394             };
6395     }
6396 });/*
6397  * Based on:
6398  * Ext JS Library 1.1.1
6399  * Copyright(c) 2006-2007, Ext JS, LLC.
6400  *
6401  * Originally Released Under LGPL - original licence link has changed is not relivant.
6402  *
6403  * Fork - LGPL
6404  * <script type="text/javascript">
6405  */
6406
6407 /**
6408  * @class Roo.data.ArrayReader
6409  * @extends Roo.data.DataReader
6410  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6411  * Each element of that Array represents a row of data fields. The
6412  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6413  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6414  * <p>
6415  * Example code:.
6416  * <pre><code>
6417 var RecordDef = Roo.data.Record.create([
6418     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6419     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6420 ]);
6421 var myReader = new Roo.data.ArrayReader({
6422     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6423 }, RecordDef);
6424 </code></pre>
6425  * <p>
6426  * This would consume an Array like this:
6427  * <pre><code>
6428 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6429   </code></pre>
6430  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6431  * @constructor
6432  * Create a new JsonReader
6433  * @param {Object} meta Metadata configuration options.
6434  * @param {Object} recordType Either an Array of field definition objects
6435  * as specified to {@link Roo.data.Record#create},
6436  * or an {@link Roo.data.Record} object
6437  * created using {@link Roo.data.Record#create}.
6438  */
6439 Roo.data.ArrayReader = function(meta, recordType){
6440     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6441 };
6442
6443 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6444     /**
6445      * Create a data block containing Roo.data.Records from an XML document.
6446      * @param {Object} o An Array of row objects which represents the dataset.
6447      * @return {Object} data A data block which is used by an Roo.data.Store object as
6448      * a cache of Roo.data.Records.
6449      */
6450     readRecords : function(o){
6451         var sid = this.meta ? this.meta.id : null;
6452         var recordType = this.recordType, fields = recordType.prototype.fields;
6453         var records = [];
6454         var root = o;
6455             for(var i = 0; i < root.length; i++){
6456                     var n = root[i];
6457                 var values = {};
6458                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6459                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6460                 var f = fields.items[j];
6461                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6462                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6463                 v = f.convert(v);
6464                 values[f.name] = v;
6465             }
6466                 var record = new recordType(values, id);
6467                 record.json = n;
6468                 records[records.length] = record;
6469             }
6470             return {
6471                 records : records,
6472                 totalRecords : records.length
6473             };
6474     }
6475 });/*
6476  * Based on:
6477  * Ext JS Library 1.1.1
6478  * Copyright(c) 2006-2007, Ext JS, LLC.
6479  *
6480  * Originally Released Under LGPL - original licence link has changed is not relivant.
6481  *
6482  * Fork - LGPL
6483  * <script type="text/javascript">
6484  */
6485
6486
6487 /**
6488  * @class Roo.data.Tree
6489  * @extends Roo.util.Observable
6490  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6491  * in the tree have most standard DOM functionality.
6492  * @constructor
6493  * @param {Node} root (optional) The root node
6494  */
6495 Roo.data.Tree = function(root){
6496    this.nodeHash = {};
6497    /**
6498     * The root node for this tree
6499     * @type Node
6500     */
6501    this.root = null;
6502    if(root){
6503        this.setRootNode(root);
6504    }
6505    this.addEvents({
6506        /**
6507         * @event append
6508         * Fires when a new child node is appended to a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The newly appended node
6512         * @param {Number} index The index of the newly appended node
6513         */
6514        "append" : true,
6515        /**
6516         * @event remove
6517         * Fires when a child node is removed from a node in this tree.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node removed
6521         */
6522        "remove" : true,
6523        /**
6524         * @event move
6525         * Fires when a node is moved to a new location in the tree
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} node The node moved
6528         * @param {Node} oldParent The old parent of this node
6529         * @param {Node} newParent The new parent of this node
6530         * @param {Number} index The index it was moved to
6531         */
6532        "move" : true,
6533        /**
6534         * @event insert
6535         * Fires when a new child node is inserted in a node in this tree.
6536         * @param {Tree} tree The owner tree
6537         * @param {Node} parent The parent node
6538         * @param {Node} node The child node inserted
6539         * @param {Node} refNode The child node the node was inserted before
6540         */
6541        "insert" : true,
6542        /**
6543         * @event beforeappend
6544         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} parent The parent node
6547         * @param {Node} node The child node to be appended
6548         */
6549        "beforeappend" : true,
6550        /**
6551         * @event beforeremove
6552         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6553         * @param {Tree} tree The owner tree
6554         * @param {Node} parent The parent node
6555         * @param {Node} node The child node to be removed
6556         */
6557        "beforeremove" : true,
6558        /**
6559         * @event beforemove
6560         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} node The node being moved
6563         * @param {Node} oldParent The parent of the node
6564         * @param {Node} newParent The new parent the node is moving to
6565         * @param {Number} index The index it is being moved to
6566         */
6567        "beforemove" : true,
6568        /**
6569         * @event beforeinsert
6570         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6571         * @param {Tree} tree The owner tree
6572         * @param {Node} parent The parent node
6573         * @param {Node} node The child node to be inserted
6574         * @param {Node} refNode The child node the node is being inserted before
6575         */
6576        "beforeinsert" : true
6577    });
6578
6579     Roo.data.Tree.superclass.constructor.call(this);
6580 };
6581
6582 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6583     pathSeparator: "/",
6584
6585     proxyNodeEvent : function(){
6586         return this.fireEvent.apply(this, arguments);
6587     },
6588
6589     /**
6590      * Returns the root node for this tree.
6591      * @return {Node}
6592      */
6593     getRootNode : function(){
6594         return this.root;
6595     },
6596
6597     /**
6598      * Sets the root node for this tree.
6599      * @param {Node} node
6600      * @return {Node}
6601      */
6602     setRootNode : function(node){
6603         this.root = node;
6604         node.ownerTree = this;
6605         node.isRoot = true;
6606         this.registerNode(node);
6607         return node;
6608     },
6609
6610     /**
6611      * Gets a node in this tree by its id.
6612      * @param {String} id
6613      * @return {Node}
6614      */
6615     getNodeById : function(id){
6616         return this.nodeHash[id];
6617     },
6618
6619     registerNode : function(node){
6620         this.nodeHash[node.id] = node;
6621     },
6622
6623     unregisterNode : function(node){
6624         delete this.nodeHash[node.id];
6625     },
6626
6627     toString : function(){
6628         return "[Tree"+(this.id?" "+this.id:"")+"]";
6629     }
6630 });
6631
6632 /**
6633  * @class Roo.data.Node
6634  * @extends Roo.util.Observable
6635  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6636  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6637  * @constructor
6638  * @param {Object} attributes The attributes/config for the node
6639  */
6640 Roo.data.Node = function(attributes){
6641     /**
6642      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6643      * @type {Object}
6644      */
6645     this.attributes = attributes || {};
6646     this.leaf = this.attributes.leaf;
6647     /**
6648      * The node id. @type String
6649      */
6650     this.id = this.attributes.id;
6651     if(!this.id){
6652         this.id = Roo.id(null, "ynode-");
6653         this.attributes.id = this.id;
6654     }
6655     /**
6656      * All child nodes of this node. @type Array
6657      */
6658     this.childNodes = [];
6659     if(!this.childNodes.indexOf){ // indexOf is a must
6660         this.childNodes.indexOf = function(o){
6661             for(var i = 0, len = this.length; i < len; i++){
6662                 if(this[i] == o) {
6663                     return i;
6664                 }
6665             }
6666             return -1;
6667         };
6668     }
6669     /**
6670      * The parent node for this node. @type Node
6671      */
6672     this.parentNode = null;
6673     /**
6674      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6675      */
6676     this.firstChild = null;
6677     /**
6678      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6679      */
6680     this.lastChild = null;
6681     /**
6682      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6683      */
6684     this.previousSibling = null;
6685     /**
6686      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6687      */
6688     this.nextSibling = null;
6689
6690     this.addEvents({
6691        /**
6692         * @event append
6693         * Fires when a new child node is appended
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The newly appended node
6697         * @param {Number} index The index of the newly appended node
6698         */
6699        "append" : true,
6700        /**
6701         * @event remove
6702         * Fires when a child node is removed
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The removed node
6706         */
6707        "remove" : true,
6708        /**
6709         * @event move
6710         * Fires when this node is moved to a new location in the tree
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} oldParent The old parent of this node
6714         * @param {Node} newParent The new parent of this node
6715         * @param {Number} index The index it was moved to
6716         */
6717        "move" : true,
6718        /**
6719         * @event insert
6720         * Fires when a new child node is inserted.
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The child node inserted
6724         * @param {Node} refNode The child node the node was inserted before
6725         */
6726        "insert" : true,
6727        /**
6728         * @event beforeappend
6729         * Fires before a new child is appended, return false to cancel the append.
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The child node to be appended
6733         */
6734        "beforeappend" : true,
6735        /**
6736         * @event beforeremove
6737         * Fires before a child is removed, return false to cancel the remove.
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} node The child node to be removed
6741         */
6742        "beforeremove" : true,
6743        /**
6744         * @event beforemove
6745         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} oldParent The parent of this node
6749         * @param {Node} newParent The new parent this node is moving to
6750         * @param {Number} index The index it is being moved to
6751         */
6752        "beforemove" : true,
6753        /**
6754         * @event beforeinsert
6755         * Fires before a new child is inserted, return false to cancel the insert.
6756         * @param {Tree} tree The owner tree
6757         * @param {Node} this This node
6758         * @param {Node} node The child node to be inserted
6759         * @param {Node} refNode The child node the node is being inserted before
6760         */
6761        "beforeinsert" : true
6762    });
6763     this.listeners = this.attributes.listeners;
6764     Roo.data.Node.superclass.constructor.call(this);
6765 };
6766
6767 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6768     fireEvent : function(evtName){
6769         // first do standard event for this node
6770         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6771             return false;
6772         }
6773         // then bubble it up to the tree if the event wasn't cancelled
6774         var ot = this.getOwnerTree();
6775         if(ot){
6776             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6777                 return false;
6778             }
6779         }
6780         return true;
6781     },
6782
6783     /**
6784      * Returns true if this node is a leaf
6785      * @return {Boolean}
6786      */
6787     isLeaf : function(){
6788         return this.leaf === true;
6789     },
6790
6791     // private
6792     setFirstChild : function(node){
6793         this.firstChild = node;
6794     },
6795
6796     //private
6797     setLastChild : function(node){
6798         this.lastChild = node;
6799     },
6800
6801
6802     /**
6803      * Returns true if this node is the last child of its parent
6804      * @return {Boolean}
6805      */
6806     isLast : function(){
6807        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6808     },
6809
6810     /**
6811      * Returns true if this node is the first child of its parent
6812      * @return {Boolean}
6813      */
6814     isFirst : function(){
6815        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6816     },
6817
6818     hasChildNodes : function(){
6819         return !this.isLeaf() && this.childNodes.length > 0;
6820     },
6821
6822     /**
6823      * Insert node(s) as the last child node of this node.
6824      * @param {Node/Array} node The node or Array of nodes to append
6825      * @return {Node} The appended node if single append, or null if an array was passed
6826      */
6827     appendChild : function(node){
6828         var multi = false;
6829         if(node instanceof Array){
6830             multi = node;
6831         }else if(arguments.length > 1){
6832             multi = arguments;
6833         }
6834         // if passed an array or multiple args do them one by one
6835         if(multi){
6836             for(var i = 0, len = multi.length; i < len; i++) {
6837                 this.appendChild(multi[i]);
6838             }
6839         }else{
6840             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6841                 return false;
6842             }
6843             var index = this.childNodes.length;
6844             var oldParent = node.parentNode;
6845             // it's a move, make sure we move it cleanly
6846             if(oldParent){
6847                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6848                     return false;
6849                 }
6850                 oldParent.removeChild(node);
6851             }
6852             index = this.childNodes.length;
6853             if(index == 0){
6854                 this.setFirstChild(node);
6855             }
6856             this.childNodes.push(node);
6857             node.parentNode = this;
6858             var ps = this.childNodes[index-1];
6859             if(ps){
6860                 node.previousSibling = ps;
6861                 ps.nextSibling = node;
6862             }else{
6863                 node.previousSibling = null;
6864             }
6865             node.nextSibling = null;
6866             this.setLastChild(node);
6867             node.setOwnerTree(this.getOwnerTree());
6868             this.fireEvent("append", this.ownerTree, this, node, index);
6869             if(oldParent){
6870                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6871             }
6872             return node;
6873         }
6874     },
6875
6876     /**
6877      * Removes a child node from this node.
6878      * @param {Node} node The node to remove
6879      * @return {Node} The removed node
6880      */
6881     removeChild : function(node){
6882         var index = this.childNodes.indexOf(node);
6883         if(index == -1){
6884             return false;
6885         }
6886         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6887             return false;
6888         }
6889
6890         // remove it from childNodes collection
6891         this.childNodes.splice(index, 1);
6892
6893         // update siblings
6894         if(node.previousSibling){
6895             node.previousSibling.nextSibling = node.nextSibling;
6896         }
6897         if(node.nextSibling){
6898             node.nextSibling.previousSibling = node.previousSibling;
6899         }
6900
6901         // update child refs
6902         if(this.firstChild == node){
6903             this.setFirstChild(node.nextSibling);
6904         }
6905         if(this.lastChild == node){
6906             this.setLastChild(node.previousSibling);
6907         }
6908
6909         node.setOwnerTree(null);
6910         // clear any references from the node
6911         node.parentNode = null;
6912         node.previousSibling = null;
6913         node.nextSibling = null;
6914         this.fireEvent("remove", this.ownerTree, this, node);
6915         return node;
6916     },
6917
6918     /**
6919      * Inserts the first node before the second node in this nodes childNodes collection.
6920      * @param {Node} node The node to insert
6921      * @param {Node} refNode The node to insert before (if null the node is appended)
6922      * @return {Node} The inserted node
6923      */
6924     insertBefore : function(node, refNode){
6925         if(!refNode){ // like standard Dom, refNode can be null for append
6926             return this.appendChild(node);
6927         }
6928         // nothing to do
6929         if(node == refNode){
6930             return false;
6931         }
6932
6933         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6934             return false;
6935         }
6936         var index = this.childNodes.indexOf(refNode);
6937         var oldParent = node.parentNode;
6938         var refIndex = index;
6939
6940         // when moving internally, indexes will change after remove
6941         if(oldParent == this && this.childNodes.indexOf(node) < index){
6942             refIndex--;
6943         }
6944
6945         // it's a move, make sure we move it cleanly
6946         if(oldParent){
6947             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6948                 return false;
6949             }
6950             oldParent.removeChild(node);
6951         }
6952         if(refIndex == 0){
6953             this.setFirstChild(node);
6954         }
6955         this.childNodes.splice(refIndex, 0, node);
6956         node.parentNode = this;
6957         var ps = this.childNodes[refIndex-1];
6958         if(ps){
6959             node.previousSibling = ps;
6960             ps.nextSibling = node;
6961         }else{
6962             node.previousSibling = null;
6963         }
6964         node.nextSibling = refNode;
6965         refNode.previousSibling = node;
6966         node.setOwnerTree(this.getOwnerTree());
6967         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6968         if(oldParent){
6969             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6970         }
6971         return node;
6972     },
6973
6974     /**
6975      * Returns the child node at the specified index.
6976      * @param {Number} index
6977      * @return {Node}
6978      */
6979     item : function(index){
6980         return this.childNodes[index];
6981     },
6982
6983     /**
6984      * Replaces one child node in this node with another.
6985      * @param {Node} newChild The replacement node
6986      * @param {Node} oldChild The node to replace
6987      * @return {Node} The replaced node
6988      */
6989     replaceChild : function(newChild, oldChild){
6990         this.insertBefore(newChild, oldChild);
6991         this.removeChild(oldChild);
6992         return oldChild;
6993     },
6994
6995     /**
6996      * Returns the index of a child node
6997      * @param {Node} node
6998      * @return {Number} The index of the node or -1 if it was not found
6999      */
7000     indexOf : function(child){
7001         return this.childNodes.indexOf(child);
7002     },
7003
7004     /**
7005      * Returns the tree this node is in.
7006      * @return {Tree}
7007      */
7008     getOwnerTree : function(){
7009         // if it doesn't have one, look for one
7010         if(!this.ownerTree){
7011             var p = this;
7012             while(p){
7013                 if(p.ownerTree){
7014                     this.ownerTree = p.ownerTree;
7015                     break;
7016                 }
7017                 p = p.parentNode;
7018             }
7019         }
7020         return this.ownerTree;
7021     },
7022
7023     /**
7024      * Returns depth of this node (the root node has a depth of 0)
7025      * @return {Number}
7026      */
7027     getDepth : function(){
7028         var depth = 0;
7029         var p = this;
7030         while(p.parentNode){
7031             ++depth;
7032             p = p.parentNode;
7033         }
7034         return depth;
7035     },
7036
7037     // private
7038     setOwnerTree : function(tree){
7039         // if it's move, we need to update everyone
7040         if(tree != this.ownerTree){
7041             if(this.ownerTree){
7042                 this.ownerTree.unregisterNode(this);
7043             }
7044             this.ownerTree = tree;
7045             var cs = this.childNodes;
7046             for(var i = 0, len = cs.length; i < len; i++) {
7047                 cs[i].setOwnerTree(tree);
7048             }
7049             if(tree){
7050                 tree.registerNode(this);
7051             }
7052         }
7053     },
7054
7055     /**
7056      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7057      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7058      * @return {String} The path
7059      */
7060     getPath : function(attr){
7061         attr = attr || "id";
7062         var p = this.parentNode;
7063         var b = [this.attributes[attr]];
7064         while(p){
7065             b.unshift(p.attributes[attr]);
7066             p = p.parentNode;
7067         }
7068         var sep = this.getOwnerTree().pathSeparator;
7069         return sep + b.join(sep);
7070     },
7071
7072     /**
7073      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7074      * function call will be the scope provided or the current node. The arguments to the function
7075      * will be the args provided or the current node. If the function returns false at any point,
7076      * the bubble is stopped.
7077      * @param {Function} fn The function to call
7078      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7079      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7080      */
7081     bubble : function(fn, scope, args){
7082         var p = this;
7083         while(p){
7084             if(fn.call(scope || p, args || p) === false){
7085                 break;
7086             }
7087             p = p.parentNode;
7088         }
7089     },
7090
7091     /**
7092      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the cascade is stopped on that branch.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     cascade : function(fn, scope, args){
7101         if(fn.call(scope || this, args || this) !== false){
7102             var cs = this.childNodes;
7103             for(var i = 0, len = cs.length; i < len; i++) {
7104                 cs[i].cascade(fn, scope, args);
7105             }
7106         }
7107     },
7108
7109     /**
7110      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7111      * function call will be the scope provided or the current node. The arguments to the function
7112      * will be the args provided or the current node. If the function returns false at any point,
7113      * the iteration stops.
7114      * @param {Function} fn The function to call
7115      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7116      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7117      */
7118     eachChild : function(fn, scope, args){
7119         var cs = this.childNodes;
7120         for(var i = 0, len = cs.length; i < len; i++) {
7121                 if(fn.call(scope || this, args || cs[i]) === false){
7122                     break;
7123                 }
7124         }
7125     },
7126
7127     /**
7128      * Finds the first child that has the attribute with the specified value.
7129      * @param {String} attribute The attribute name
7130      * @param {Mixed} value The value to search for
7131      * @return {Node} The found child or null if none was found
7132      */
7133     findChild : function(attribute, value){
7134         var cs = this.childNodes;
7135         for(var i = 0, len = cs.length; i < len; i++) {
7136                 if(cs[i].attributes[attribute] == value){
7137                     return cs[i];
7138                 }
7139         }
7140         return null;
7141     },
7142
7143     /**
7144      * Finds the first child by a custom function. The child matches if the function passed
7145      * returns true.
7146      * @param {Function} fn
7147      * @param {Object} scope (optional)
7148      * @return {Node} The found child or null if none was found
7149      */
7150     findChildBy : function(fn, scope){
7151         var cs = this.childNodes;
7152         for(var i = 0, len = cs.length; i < len; i++) {
7153                 if(fn.call(scope||cs[i], cs[i]) === true){
7154                     return cs[i];
7155                 }
7156         }
7157         return null;
7158     },
7159
7160     /**
7161      * Sorts this nodes children using the supplied sort function
7162      * @param {Function} fn
7163      * @param {Object} scope (optional)
7164      */
7165     sort : function(fn, scope){
7166         var cs = this.childNodes;
7167         var len = cs.length;
7168         if(len > 0){
7169             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7170             cs.sort(sortFn);
7171             for(var i = 0; i < len; i++){
7172                 var n = cs[i];
7173                 n.previousSibling = cs[i-1];
7174                 n.nextSibling = cs[i+1];
7175                 if(i == 0){
7176                     this.setFirstChild(n);
7177                 }
7178                 if(i == len-1){
7179                     this.setLastChild(n);
7180                 }
7181             }
7182         }
7183     },
7184
7185     /**
7186      * Returns true if this node is an ancestor (at any point) of the passed node.
7187      * @param {Node} node
7188      * @return {Boolean}
7189      */
7190     contains : function(node){
7191         return node.isAncestor(this);
7192     },
7193
7194     /**
7195      * Returns true if the passed node is an ancestor (at any point) of this node.
7196      * @param {Node} node
7197      * @return {Boolean}
7198      */
7199     isAncestor : function(node){
7200         var p = this.parentNode;
7201         while(p){
7202             if(p == node){
7203                 return true;
7204             }
7205             p = p.parentNode;
7206         }
7207         return false;
7208     },
7209
7210     toString : function(){
7211         return "[Node"+(this.id?" "+this.id:"")+"]";
7212     }
7213 });/*
7214  * Based on:
7215  * Ext JS Library 1.1.1
7216  * Copyright(c) 2006-2007, Ext JS, LLC.
7217  *
7218  * Originally Released Under LGPL - original licence link has changed is not relivant.
7219  *
7220  * Fork - LGPL
7221  * <script type="text/javascript">
7222  */
7223  
7224
7225 /**
7226  * @class Roo.ComponentMgr
7227  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7228  * @singleton
7229  */
7230 Roo.ComponentMgr = function(){
7231     var all = new Roo.util.MixedCollection();
7232
7233     return {
7234         /**
7235          * Registers a component.
7236          * @param {Roo.Component} c The component
7237          */
7238         register : function(c){
7239             all.add(c);
7240         },
7241
7242         /**
7243          * Unregisters a component.
7244          * @param {Roo.Component} c The component
7245          */
7246         unregister : function(c){
7247             all.remove(c);
7248         },
7249
7250         /**
7251          * Returns a component by id
7252          * @param {String} id The component id
7253          */
7254         get : function(id){
7255             return all.get(id);
7256         },
7257
7258         /**
7259          * Registers a function that will be called when a specified component is added to ComponentMgr
7260          * @param {String} id The component id
7261          * @param {Funtction} fn The callback function
7262          * @param {Object} scope The scope of the callback
7263          */
7264         onAvailable : function(id, fn, scope){
7265             all.on("add", function(index, o){
7266                 if(o.id == id){
7267                     fn.call(scope || o, o);
7268                     all.un("add", fn, scope);
7269                 }
7270             });
7271         }
7272     };
7273 }();/*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283  
7284 /**
7285  * @class Roo.Component
7286  * @extends Roo.util.Observable
7287  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7288  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7289  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7290  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7291  * All visual components (widgets) that require rendering into a layout should subclass Component.
7292  * @constructor
7293  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7294  * 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
7295  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7296  */
7297 Roo.Component = function(config){
7298     config = config || {};
7299     if(config.tagName || config.dom || typeof config == "string"){ // element object
7300         config = {el: config, id: config.id || config};
7301     }
7302     this.initialConfig = config;
7303
7304     Roo.apply(this, config);
7305     this.addEvents({
7306         /**
7307          * @event disable
7308          * Fires after the component is disabled.
7309              * @param {Roo.Component} this
7310              */
7311         disable : true,
7312         /**
7313          * @event enable
7314          * Fires after the component is enabled.
7315              * @param {Roo.Component} this
7316              */
7317         enable : true,
7318         /**
7319          * @event beforeshow
7320          * Fires before the component is shown.  Return false to stop the show.
7321              * @param {Roo.Component} this
7322              */
7323         beforeshow : true,
7324         /**
7325          * @event show
7326          * Fires after the component is shown.
7327              * @param {Roo.Component} this
7328              */
7329         show : true,
7330         /**
7331          * @event beforehide
7332          * Fires before the component is hidden. Return false to stop the hide.
7333              * @param {Roo.Component} this
7334              */
7335         beforehide : true,
7336         /**
7337          * @event hide
7338          * Fires after the component is hidden.
7339              * @param {Roo.Component} this
7340              */
7341         hide : true,
7342         /**
7343          * @event beforerender
7344          * Fires before the component is rendered. Return false to stop the render.
7345              * @param {Roo.Component} this
7346              */
7347         beforerender : true,
7348         /**
7349          * @event render
7350          * Fires after the component is rendered.
7351              * @param {Roo.Component} this
7352              */
7353         render : true,
7354         /**
7355          * @event beforedestroy
7356          * Fires before the component is destroyed. Return false to stop the destroy.
7357              * @param {Roo.Component} this
7358              */
7359         beforedestroy : true,
7360         /**
7361          * @event destroy
7362          * Fires after the component is destroyed.
7363              * @param {Roo.Component} this
7364              */
7365         destroy : true
7366     });
7367     if(!this.id){
7368         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7369     }
7370     Roo.ComponentMgr.register(this);
7371     Roo.Component.superclass.constructor.call(this);
7372     this.initComponent();
7373     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7374         this.render(this.renderTo);
7375         delete this.renderTo;
7376     }
7377 };
7378
7379 // private
7380 Roo.Component.AUTO_ID = 1000;
7381
7382 Roo.extend(Roo.Component, Roo.util.Observable, {
7383     /**
7384      * @property {Boolean} hidden
7385      * true if this component is hidden. Read-only.
7386      */
7387     hidden : false,
7388     /**
7389      * true if this component is disabled. Read-only.
7390      */
7391     disabled : false,
7392     /**
7393      * true if this component has been rendered. Read-only.
7394      */
7395     rendered : false,
7396     
7397     /** @cfg {String} disableClass
7398      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7399      */
7400     disabledClass : "x-item-disabled",
7401         /** @cfg {Boolean} allowDomMove
7402          * Whether the component can move the Dom node when rendering (defaults to true).
7403          */
7404     allowDomMove : true,
7405     /** @cfg {String} hideMode
7406      * How this component should hidden. Supported values are
7407      * "visibility" (css visibility), "offsets" (negative offset position) and
7408      * "display" (css display) - defaults to "display".
7409      */
7410     hideMode: 'display',
7411
7412     // private
7413     ctype : "Roo.Component",
7414
7415     /** @cfg {String} actionMode 
7416      * which property holds the element that used for  hide() / show() / disable() / enable()
7417      * default is 'el' 
7418      */
7419     actionMode : "el",
7420
7421     // private
7422     getActionEl : function(){
7423         return this[this.actionMode];
7424     },
7425
7426     initComponent : Roo.emptyFn,
7427     /**
7428      * If this is a lazy rendering component, render it to its container element.
7429      * @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.
7430      */
7431     render : function(container, position){
7432         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7433             if(!container && this.el){
7434                 this.el = Roo.get(this.el);
7435                 container = this.el.dom.parentNode;
7436                 this.allowDomMove = false;
7437             }
7438             this.container = Roo.get(container);
7439             this.rendered = true;
7440             if(position !== undefined){
7441                 if(typeof position == 'number'){
7442                     position = this.container.dom.childNodes[position];
7443                 }else{
7444                     position = Roo.getDom(position);
7445                 }
7446             }
7447             this.onRender(this.container, position || null);
7448             if(this.cls){
7449                 this.el.addClass(this.cls);
7450                 delete this.cls;
7451             }
7452             if(this.style){
7453                 this.el.applyStyles(this.style);
7454                 delete this.style;
7455             }
7456             this.fireEvent("render", this);
7457             this.afterRender(this.container);
7458             if(this.hidden){
7459                 this.hide();
7460             }
7461             if(this.disabled){
7462                 this.disable();
7463             }
7464         }
7465         return this;
7466     },
7467
7468     // private
7469     // default function is not really useful
7470     onRender : function(ct, position){
7471         if(this.el){
7472             this.el = Roo.get(this.el);
7473             if(this.allowDomMove !== false){
7474                 ct.dom.insertBefore(this.el.dom, position);
7475             }
7476         }
7477     },
7478
7479     // private
7480     getAutoCreate : function(){
7481         var cfg = typeof this.autoCreate == "object" ?
7482                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7483         if(this.id && !cfg.id){
7484             cfg.id = this.id;
7485         }
7486         return cfg;
7487     },
7488
7489     // private
7490     afterRender : Roo.emptyFn,
7491
7492     /**
7493      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7494      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7495      */
7496     destroy : function(){
7497         if(this.fireEvent("beforedestroy", this) !== false){
7498             this.purgeListeners();
7499             this.beforeDestroy();
7500             if(this.rendered){
7501                 this.el.removeAllListeners();
7502                 this.el.remove();
7503                 if(this.actionMode == "container"){
7504                     this.container.remove();
7505                 }
7506             }
7507             this.onDestroy();
7508             Roo.ComponentMgr.unregister(this);
7509             this.fireEvent("destroy", this);
7510         }
7511     },
7512
7513         // private
7514     beforeDestroy : function(){
7515
7516     },
7517
7518         // private
7519         onDestroy : function(){
7520
7521     },
7522
7523     /**
7524      * Returns the underlying {@link Roo.Element}.
7525      * @return {Roo.Element} The element
7526      */
7527     getEl : function(){
7528         return this.el;
7529     },
7530
7531     /**
7532      * Returns the id of this component.
7533      * @return {String}
7534      */
7535     getId : function(){
7536         return this.id;
7537     },
7538
7539     /**
7540      * Try to focus this component.
7541      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7542      * @return {Roo.Component} this
7543      */
7544     focus : function(selectText){
7545         if(this.rendered){
7546             this.el.focus();
7547             if(selectText === true){
7548                 this.el.dom.select();
7549             }
7550         }
7551         return this;
7552     },
7553
7554     // private
7555     blur : function(){
7556         if(this.rendered){
7557             this.el.blur();
7558         }
7559         return this;
7560     },
7561
7562     /**
7563      * Disable this component.
7564      * @return {Roo.Component} this
7565      */
7566     disable : function(){
7567         if(this.rendered){
7568             this.onDisable();
7569         }
7570         this.disabled = true;
7571         this.fireEvent("disable", this);
7572         return this;
7573     },
7574
7575         // private
7576     onDisable : function(){
7577         this.getActionEl().addClass(this.disabledClass);
7578         this.el.dom.disabled = true;
7579     },
7580
7581     /**
7582      * Enable this component.
7583      * @return {Roo.Component} this
7584      */
7585     enable : function(){
7586         if(this.rendered){
7587             this.onEnable();
7588         }
7589         this.disabled = false;
7590         this.fireEvent("enable", this);
7591         return this;
7592     },
7593
7594         // private
7595     onEnable : function(){
7596         this.getActionEl().removeClass(this.disabledClass);
7597         this.el.dom.disabled = false;
7598     },
7599
7600     /**
7601      * Convenience function for setting disabled/enabled by boolean.
7602      * @param {Boolean} disabled
7603      */
7604     setDisabled : function(disabled){
7605         this[disabled ? "disable" : "enable"]();
7606     },
7607
7608     /**
7609      * Show this component.
7610      * @return {Roo.Component} this
7611      */
7612     show: function(){
7613         if(this.fireEvent("beforeshow", this) !== false){
7614             this.hidden = false;
7615             if(this.rendered){
7616                 this.onShow();
7617             }
7618             this.fireEvent("show", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onShow : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "visible";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.removeClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "";
7632         }
7633     },
7634
7635     /**
7636      * Hide this component.
7637      * @return {Roo.Component} this
7638      */
7639     hide: function(){
7640         if(this.fireEvent("beforehide", this) !== false){
7641             this.hidden = true;
7642             if(this.rendered){
7643                 this.onHide();
7644             }
7645             this.fireEvent("hide", this);
7646         }
7647         return this;
7648     },
7649
7650     // private
7651     onHide : function(){
7652         var ae = this.getActionEl();
7653         if(this.hideMode == 'visibility'){
7654             ae.dom.style.visibility = "hidden";
7655         }else if(this.hideMode == 'offsets'){
7656             ae.addClass('x-hidden');
7657         }else{
7658             ae.dom.style.display = "none";
7659         }
7660     },
7661
7662     /**
7663      * Convenience function to hide or show this component by boolean.
7664      * @param {Boolean} visible True to show, false to hide
7665      * @return {Roo.Component} this
7666      */
7667     setVisible: function(visible){
7668         if(visible) {
7669             this.show();
7670         }else{
7671             this.hide();
7672         }
7673         return this;
7674     },
7675
7676     /**
7677      * Returns true if this component is visible.
7678      */
7679     isVisible : function(){
7680         return this.getActionEl().isVisible();
7681     },
7682
7683     cloneConfig : function(overrides){
7684         overrides = overrides || {};
7685         var id = overrides.id || Roo.id();
7686         var cfg = Roo.applyIf(overrides, this.initialConfig);
7687         cfg.id = id; // prevent dup id
7688         return new this.constructor(cfg);
7689     }
7690 });/*
7691  * Based on:
7692  * Ext JS Library 1.1.1
7693  * Copyright(c) 2006-2007, Ext JS, LLC.
7694  *
7695  * Originally Released Under LGPL - original licence link has changed is not relivant.
7696  *
7697  * Fork - LGPL
7698  * <script type="text/javascript">
7699  */
7700  (function(){ 
7701 /**
7702  * @class Roo.Layer
7703  * @extends Roo.Element
7704  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7705  * automatic maintaining of shadow/shim positions.
7706  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7707  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7708  * you can pass a string with a CSS class name. False turns off the shadow.
7709  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7710  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7711  * @cfg {String} cls CSS class to add to the element
7712  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7713  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7714  * @constructor
7715  * @param {Object} config An object with config options.
7716  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7717  */
7718
7719 Roo.Layer = function(config, existingEl){
7720     config = config || {};
7721     var dh = Roo.DomHelper;
7722     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7723     if(existingEl){
7724         this.dom = Roo.getDom(existingEl);
7725     }
7726     if(!this.dom){
7727         var o = config.dh || {tag: "div", cls: "x-layer"};
7728         this.dom = dh.append(pel, o);
7729     }
7730     if(config.cls){
7731         this.addClass(config.cls);
7732     }
7733     this.constrain = config.constrain !== false;
7734     this.visibilityMode = Roo.Element.VISIBILITY;
7735     if(config.id){
7736         this.id = this.dom.id = config.id;
7737     }else{
7738         this.id = Roo.id(this.dom);
7739     }
7740     this.zindex = config.zindex || this.getZIndex();
7741     this.position("absolute", this.zindex);
7742     if(config.shadow){
7743         this.shadowOffset = config.shadowOffset || 4;
7744         this.shadow = new Roo.Shadow({
7745             offset : this.shadowOffset,
7746             mode : config.shadow
7747         });
7748     }else{
7749         this.shadowOffset = 0;
7750     }
7751     this.useShim = config.shim !== false && Roo.useShims;
7752     this.useDisplay = config.useDisplay;
7753     this.hide();
7754 };
7755
7756 var supr = Roo.Element.prototype;
7757
7758 // shims are shared among layer to keep from having 100 iframes
7759 var shims = [];
7760
7761 Roo.extend(Roo.Layer, Roo.Element, {
7762
7763     getZIndex : function(){
7764         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7765     },
7766
7767     getShim : function(){
7768         if(!this.useShim){
7769             return null;
7770         }
7771         if(this.shim){
7772             return this.shim;
7773         }
7774         var shim = shims.shift();
7775         if(!shim){
7776             shim = this.createShim();
7777             shim.enableDisplayMode('block');
7778             shim.dom.style.display = 'none';
7779             shim.dom.style.visibility = 'visible';
7780         }
7781         var pn = this.dom.parentNode;
7782         if(shim.dom.parentNode != pn){
7783             pn.insertBefore(shim.dom, this.dom);
7784         }
7785         shim.setStyle('z-index', this.getZIndex()-2);
7786         this.shim = shim;
7787         return shim;
7788     },
7789
7790     hideShim : function(){
7791         if(this.shim){
7792             this.shim.setDisplayed(false);
7793             shims.push(this.shim);
7794             delete this.shim;
7795         }
7796     },
7797
7798     disableShadow : function(){
7799         if(this.shadow){
7800             this.shadowDisabled = true;
7801             this.shadow.hide();
7802             this.lastShadowOffset = this.shadowOffset;
7803             this.shadowOffset = 0;
7804         }
7805     },
7806
7807     enableShadow : function(show){
7808         if(this.shadow){
7809             this.shadowDisabled = false;
7810             this.shadowOffset = this.lastShadowOffset;
7811             delete this.lastShadowOffset;
7812             if(show){
7813                 this.sync(true);
7814             }
7815         }
7816     },
7817
7818     // private
7819     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7820     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7821     sync : function(doShow){
7822         var sw = this.shadow;
7823         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7824             var sh = this.getShim();
7825
7826             var w = this.getWidth(),
7827                 h = this.getHeight();
7828
7829             var l = this.getLeft(true),
7830                 t = this.getTop(true);
7831
7832             if(sw && !this.shadowDisabled){
7833                 if(doShow && !sw.isVisible()){
7834                     sw.show(this);
7835                 }else{
7836                     sw.realign(l, t, w, h);
7837                 }
7838                 if(sh){
7839                     if(doShow){
7840                        sh.show();
7841                     }
7842                     // fit the shim behind the shadow, so it is shimmed too
7843                     var a = sw.adjusts, s = sh.dom.style;
7844                     s.left = (Math.min(l, l+a.l))+"px";
7845                     s.top = (Math.min(t, t+a.t))+"px";
7846                     s.width = (w+a.w)+"px";
7847                     s.height = (h+a.h)+"px";
7848                 }
7849             }else if(sh){
7850                 if(doShow){
7851                    sh.show();
7852                 }
7853                 sh.setSize(w, h);
7854                 sh.setLeftTop(l, t);
7855             }
7856             
7857         }
7858     },
7859
7860     // private
7861     destroy : function(){
7862         this.hideShim();
7863         if(this.shadow){
7864             this.shadow.hide();
7865         }
7866         this.removeAllListeners();
7867         var pn = this.dom.parentNode;
7868         if(pn){
7869             pn.removeChild(this.dom);
7870         }
7871         Roo.Element.uncache(this.id);
7872     },
7873
7874     remove : function(){
7875         this.destroy();
7876     },
7877
7878     // private
7879     beginUpdate : function(){
7880         this.updating = true;
7881     },
7882
7883     // private
7884     endUpdate : function(){
7885         this.updating = false;
7886         this.sync(true);
7887     },
7888
7889     // private
7890     hideUnders : function(negOffset){
7891         if(this.shadow){
7892             this.shadow.hide();
7893         }
7894         this.hideShim();
7895     },
7896
7897     // private
7898     constrainXY : function(){
7899         if(this.constrain){
7900             var vw = Roo.lib.Dom.getViewWidth(),
7901                 vh = Roo.lib.Dom.getViewHeight();
7902             var s = Roo.get(document).getScroll();
7903
7904             var xy = this.getXY();
7905             var x = xy[0], y = xy[1];   
7906             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7907             // only move it if it needs it
7908             var moved = false;
7909             // first validate right/bottom
7910             if((x + w) > vw+s.left){
7911                 x = vw - w - this.shadowOffset;
7912                 moved = true;
7913             }
7914             if((y + h) > vh+s.top){
7915                 y = vh - h - this.shadowOffset;
7916                 moved = true;
7917             }
7918             // then make sure top/left isn't negative
7919             if(x < s.left){
7920                 x = s.left;
7921                 moved = true;
7922             }
7923             if(y < s.top){
7924                 y = s.top;
7925                 moved = true;
7926             }
7927             if(moved){
7928                 if(this.avoidY){
7929                     var ay = this.avoidY;
7930                     if(y <= ay && (y+h) >= ay){
7931                         y = ay-h-5;   
7932                     }
7933                 }
7934                 xy = [x, y];
7935                 this.storeXY(xy);
7936                 supr.setXY.call(this, xy);
7937                 this.sync();
7938             }
7939         }
7940     },
7941
7942     isVisible : function(){
7943         return this.visible;    
7944     },
7945
7946     // private
7947     showAction : function(){
7948         this.visible = true; // track visibility to prevent getStyle calls
7949         if(this.useDisplay === true){
7950             this.setDisplayed("");
7951         }else if(this.lastXY){
7952             supr.setXY.call(this, this.lastXY);
7953         }else if(this.lastLT){
7954             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7955         }
7956     },
7957
7958     // private
7959     hideAction : function(){
7960         this.visible = false;
7961         if(this.useDisplay === true){
7962             this.setDisplayed(false);
7963         }else{
7964             this.setLeftTop(-10000,-10000);
7965         }
7966     },
7967
7968     // overridden Element method
7969     setVisible : function(v, a, d, c, e){
7970         if(v){
7971             this.showAction();
7972         }
7973         if(a && v){
7974             var cb = function(){
7975                 this.sync(true);
7976                 if(c){
7977                     c();
7978                 }
7979             }.createDelegate(this);
7980             supr.setVisible.call(this, true, true, d, cb, e);
7981         }else{
7982             if(!v){
7983                 this.hideUnders(true);
7984             }
7985             var cb = c;
7986             if(a){
7987                 cb = function(){
7988                     this.hideAction();
7989                     if(c){
7990                         c();
7991                     }
7992                 }.createDelegate(this);
7993             }
7994             supr.setVisible.call(this, v, a, d, cb, e);
7995             if(v){
7996                 this.sync(true);
7997             }else if(!a){
7998                 this.hideAction();
7999             }
8000         }
8001     },
8002
8003     storeXY : function(xy){
8004         delete this.lastLT;
8005         this.lastXY = xy;
8006     },
8007
8008     storeLeftTop : function(left, top){
8009         delete this.lastXY;
8010         this.lastLT = [left, top];
8011     },
8012
8013     // private
8014     beforeFx : function(){
8015         this.beforeAction();
8016         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8017     },
8018
8019     // private
8020     afterFx : function(){
8021         Roo.Layer.superclass.afterFx.apply(this, arguments);
8022         this.sync(this.isVisible());
8023     },
8024
8025     // private
8026     beforeAction : function(){
8027         if(!this.updating && this.shadow){
8028             this.shadow.hide();
8029         }
8030     },
8031
8032     // overridden Element method
8033     setLeft : function(left){
8034         this.storeLeftTop(left, this.getTop(true));
8035         supr.setLeft.apply(this, arguments);
8036         this.sync();
8037     },
8038
8039     setTop : function(top){
8040         this.storeLeftTop(this.getLeft(true), top);
8041         supr.setTop.apply(this, arguments);
8042         this.sync();
8043     },
8044
8045     setLeftTop : function(left, top){
8046         this.storeLeftTop(left, top);
8047         supr.setLeftTop.apply(this, arguments);
8048         this.sync();
8049     },
8050
8051     setXY : function(xy, a, d, c, e){
8052         this.fixDisplay();
8053         this.beforeAction();
8054         this.storeXY(xy);
8055         var cb = this.createCB(c);
8056         supr.setXY.call(this, xy, a, d, cb, e);
8057         if(!a){
8058             cb();
8059         }
8060     },
8061
8062     // private
8063     createCB : function(c){
8064         var el = this;
8065         return function(){
8066             el.constrainXY();
8067             el.sync(true);
8068             if(c){
8069                 c();
8070             }
8071         };
8072     },
8073
8074     // overridden Element method
8075     setX : function(x, a, d, c, e){
8076         this.setXY([x, this.getY()], a, d, c, e);
8077     },
8078
8079     // overridden Element method
8080     setY : function(y, a, d, c, e){
8081         this.setXY([this.getX(), y], a, d, c, e);
8082     },
8083
8084     // overridden Element method
8085     setSize : function(w, h, a, d, c, e){
8086         this.beforeAction();
8087         var cb = this.createCB(c);
8088         supr.setSize.call(this, w, h, a, d, cb, e);
8089         if(!a){
8090             cb();
8091         }
8092     },
8093
8094     // overridden Element method
8095     setWidth : function(w, a, d, c, e){
8096         this.beforeAction();
8097         var cb = this.createCB(c);
8098         supr.setWidth.call(this, w, a, d, cb, e);
8099         if(!a){
8100             cb();
8101         }
8102     },
8103
8104     // overridden Element method
8105     setHeight : function(h, a, d, c, e){
8106         this.beforeAction();
8107         var cb = this.createCB(c);
8108         supr.setHeight.call(this, h, a, d, cb, e);
8109         if(!a){
8110             cb();
8111         }
8112     },
8113
8114     // overridden Element method
8115     setBounds : function(x, y, w, h, a, d, c, e){
8116         this.beforeAction();
8117         var cb = this.createCB(c);
8118         if(!a){
8119             this.storeXY([x, y]);
8120             supr.setXY.call(this, [x, y]);
8121             supr.setSize.call(this, w, h, a, d, cb, e);
8122             cb();
8123         }else{
8124             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8125         }
8126         return this;
8127     },
8128     
8129     /**
8130      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8131      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8132      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8133      * @param {Number} zindex The new z-index to set
8134      * @return {this} The Layer
8135      */
8136     setZIndex : function(zindex){
8137         this.zindex = zindex;
8138         this.setStyle("z-index", zindex + 2);
8139         if(this.shadow){
8140             this.shadow.setZIndex(zindex + 1);
8141         }
8142         if(this.shim){
8143             this.shim.setStyle("z-index", zindex);
8144         }
8145     }
8146 });
8147 })();/*
8148  * Based on:
8149  * Ext JS Library 1.1.1
8150  * Copyright(c) 2006-2007, Ext JS, LLC.
8151  *
8152  * Originally Released Under LGPL - original licence link has changed is not relivant.
8153  *
8154  * Fork - LGPL
8155  * <script type="text/javascript">
8156  */
8157
8158
8159 /**
8160  * @class Roo.Shadow
8161  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8162  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8163  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8164  * @constructor
8165  * Create a new Shadow
8166  * @param {Object} config The config object
8167  */
8168 Roo.Shadow = function(config){
8169     Roo.apply(this, config);
8170     if(typeof this.mode != "string"){
8171         this.mode = this.defaultMode;
8172     }
8173     var o = this.offset, a = {h: 0};
8174     var rad = Math.floor(this.offset/2);
8175     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8176         case "drop":
8177             a.w = 0;
8178             a.l = a.t = o;
8179             a.t -= 1;
8180             if(Roo.isIE){
8181                 a.l -= this.offset + rad;
8182                 a.t -= this.offset + rad;
8183                 a.w -= rad;
8184                 a.h -= rad;
8185                 a.t += 1;
8186             }
8187         break;
8188         case "sides":
8189             a.w = (o*2);
8190             a.l = -o;
8191             a.t = o-1;
8192             if(Roo.isIE){
8193                 a.l -= (this.offset - rad);
8194                 a.t -= this.offset + rad;
8195                 a.l += 1;
8196                 a.w -= (this.offset - rad)*2;
8197                 a.w -= rad + 1;
8198                 a.h -= 1;
8199             }
8200         break;
8201         case "frame":
8202             a.w = a.h = (o*2);
8203             a.l = a.t = -o;
8204             a.t += 1;
8205             a.h -= 2;
8206             if(Roo.isIE){
8207                 a.l -= (this.offset - rad);
8208                 a.t -= (this.offset - rad);
8209                 a.l += 1;
8210                 a.w -= (this.offset + rad + 1);
8211                 a.h -= (this.offset + rad);
8212                 a.h += 1;
8213             }
8214         break;
8215     };
8216
8217     this.adjusts = a;
8218 };
8219
8220 Roo.Shadow.prototype = {
8221     /**
8222      * @cfg {String} mode
8223      * The shadow display mode.  Supports the following options:<br />
8224      * sides: Shadow displays on both sides and bottom only<br />
8225      * frame: Shadow displays equally on all four sides<br />
8226      * drop: Traditional bottom-right drop shadow (default)
8227      */
8228     /**
8229      * @cfg {String} offset
8230      * The number of pixels to offset the shadow from the element (defaults to 4)
8231      */
8232     offset: 4,
8233
8234     // private
8235     defaultMode: "drop",
8236
8237     /**
8238      * Displays the shadow under the target element
8239      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8240      */
8241     show : function(target){
8242         target = Roo.get(target);
8243         if(!this.el){
8244             this.el = Roo.Shadow.Pool.pull();
8245             if(this.el.dom.nextSibling != target.dom){
8246                 this.el.insertBefore(target);
8247             }
8248         }
8249         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8250         if(Roo.isIE){
8251             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8252         }
8253         this.realign(
8254             target.getLeft(true),
8255             target.getTop(true),
8256             target.getWidth(),
8257             target.getHeight()
8258         );
8259         this.el.dom.style.display = "block";
8260     },
8261
8262     /**
8263      * Returns true if the shadow is visible, else false
8264      */
8265     isVisible : function(){
8266         return this.el ? true : false;  
8267     },
8268
8269     /**
8270      * Direct alignment when values are already available. Show must be called at least once before
8271      * calling this method to ensure it is initialized.
8272      * @param {Number} left The target element left position
8273      * @param {Number} top The target element top position
8274      * @param {Number} width The target element width
8275      * @param {Number} height The target element height
8276      */
8277     realign : function(l, t, w, h){
8278         if(!this.el){
8279             return;
8280         }
8281         var a = this.adjusts, d = this.el.dom, s = d.style;
8282         var iea = 0;
8283         s.left = (l+a.l)+"px";
8284         s.top = (t+a.t)+"px";
8285         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8286  
8287         if(s.width != sws || s.height != shs){
8288             s.width = sws;
8289             s.height = shs;
8290             if(!Roo.isIE){
8291                 var cn = d.childNodes;
8292                 var sww = Math.max(0, (sw-12))+"px";
8293                 cn[0].childNodes[1].style.width = sww;
8294                 cn[1].childNodes[1].style.width = sww;
8295                 cn[2].childNodes[1].style.width = sww;
8296                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8297             }
8298         }
8299     },
8300
8301     /**
8302      * Hides this shadow
8303      */
8304     hide : function(){
8305         if(this.el){
8306             this.el.dom.style.display = "none";
8307             Roo.Shadow.Pool.push(this.el);
8308             delete this.el;
8309         }
8310     },
8311
8312     /**
8313      * Adjust the z-index of this shadow
8314      * @param {Number} zindex The new z-index
8315      */
8316     setZIndex : function(z){
8317         this.zIndex = z;
8318         if(this.el){
8319             this.el.setStyle("z-index", z);
8320         }
8321     }
8322 };
8323
8324 // Private utility class that manages the internal Shadow cache
8325 Roo.Shadow.Pool = function(){
8326     var p = [];
8327     var markup = Roo.isIE ?
8328                  '<div class="x-ie-shadow"></div>' :
8329                  '<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>';
8330     return {
8331         pull : function(){
8332             var sh = p.shift();
8333             if(!sh){
8334                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8335                 sh.autoBoxAdjust = false;
8336             }
8337             return sh;
8338         },
8339
8340         push : function(sh){
8341             p.push(sh);
8342         }
8343     };
8344 }();/*
8345  * Based on:
8346  * Ext JS Library 1.1.1
8347  * Copyright(c) 2006-2007, Ext JS, LLC.
8348  *
8349  * Originally Released Under LGPL - original licence link has changed is not relivant.
8350  *
8351  * Fork - LGPL
8352  * <script type="text/javascript">
8353  */
8354
8355 /**
8356  * @class Roo.BoxComponent
8357  * @extends Roo.Component
8358  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8359  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8360  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8361  * layout containers.
8362  * @constructor
8363  * @param {Roo.Element/String/Object} config The configuration options.
8364  */
8365 Roo.BoxComponent = function(config){
8366     Roo.Component.call(this, config);
8367     this.addEvents({
8368         /**
8369          * @event resize
8370          * Fires after the component is resized.
8371              * @param {Roo.Component} this
8372              * @param {Number} adjWidth The box-adjusted width that was set
8373              * @param {Number} adjHeight The box-adjusted height that was set
8374              * @param {Number} rawWidth The width that was originally specified
8375              * @param {Number} rawHeight The height that was originally specified
8376              */
8377         resize : true,
8378         /**
8379          * @event move
8380          * Fires after the component is moved.
8381              * @param {Roo.Component} this
8382              * @param {Number} x The new x position
8383              * @param {Number} y The new y position
8384              */
8385         move : true
8386     });
8387 };
8388
8389 Roo.extend(Roo.BoxComponent, Roo.Component, {
8390     // private, set in afterRender to signify that the component has been rendered
8391     boxReady : false,
8392     // private, used to defer height settings to subclasses
8393     deferHeight: false,
8394     /** @cfg {Number} width
8395      * width (optional) size of component
8396      */
8397      /** @cfg {Number} height
8398      * height (optional) size of component
8399      */
8400      
8401     /**
8402      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8403      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8404      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8405      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8406      * @return {Roo.BoxComponent} this
8407      */
8408     setSize : function(w, h){
8409         // support for standard size objects
8410         if(typeof w == 'object'){
8411             h = w.height;
8412             w = w.width;
8413         }
8414         // not rendered
8415         if(!this.boxReady){
8416             this.width = w;
8417             this.height = h;
8418             return this;
8419         }
8420
8421         // prevent recalcs when not needed
8422         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8423             return this;
8424         }
8425         this.lastSize = {width: w, height: h};
8426
8427         var adj = this.adjustSize(w, h);
8428         var aw = adj.width, ah = adj.height;
8429         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8430             var rz = this.getResizeEl();
8431             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8432                 rz.setSize(aw, ah);
8433             }else if(!this.deferHeight && ah !== undefined){
8434                 rz.setHeight(ah);
8435             }else if(aw !== undefined){
8436                 rz.setWidth(aw);
8437             }
8438             this.onResize(aw, ah, w, h);
8439             this.fireEvent('resize', this, aw, ah, w, h);
8440         }
8441         return this;
8442     },
8443
8444     /**
8445      * Gets the current size of the component's underlying element.
8446      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8447      */
8448     getSize : function(){
8449         return this.el.getSize();
8450     },
8451
8452     /**
8453      * Gets the current XY position of the component's underlying element.
8454      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8455      * @return {Array} The XY position of the element (e.g., [100, 200])
8456      */
8457     getPosition : function(local){
8458         if(local === true){
8459             return [this.el.getLeft(true), this.el.getTop(true)];
8460         }
8461         return this.xy || this.el.getXY();
8462     },
8463
8464     /**
8465      * Gets the current box measurements of the component's underlying element.
8466      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8467      * @returns {Object} box An object in the format {x, y, width, height}
8468      */
8469     getBox : function(local){
8470         var s = this.el.getSize();
8471         if(local){
8472             s.x = this.el.getLeft(true);
8473             s.y = this.el.getTop(true);
8474         }else{
8475             var xy = this.xy || this.el.getXY();
8476             s.x = xy[0];
8477             s.y = xy[1];
8478         }
8479         return s;
8480     },
8481
8482     /**
8483      * Sets the current box measurements of the component's underlying element.
8484      * @param {Object} box An object in the format {x, y, width, height}
8485      * @returns {Roo.BoxComponent} this
8486      */
8487     updateBox : function(box){
8488         this.setSize(box.width, box.height);
8489         this.setPagePosition(box.x, box.y);
8490         return this;
8491     },
8492
8493     // protected
8494     getResizeEl : function(){
8495         return this.resizeEl || this.el;
8496     },
8497
8498     // protected
8499     getPositionEl : function(){
8500         return this.positionEl || this.el;
8501     },
8502
8503     /**
8504      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8505      * This method fires the move event.
8506      * @param {Number} left The new left
8507      * @param {Number} top The new top
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     setPosition : function(x, y){
8511         this.x = x;
8512         this.y = y;
8513         if(!this.boxReady){
8514             return this;
8515         }
8516         var adj = this.adjustPosition(x, y);
8517         var ax = adj.x, ay = adj.y;
8518
8519         var el = this.getPositionEl();
8520         if(ax !== undefined || ay !== undefined){
8521             if(ax !== undefined && ay !== undefined){
8522                 el.setLeftTop(ax, ay);
8523             }else if(ax !== undefined){
8524                 el.setLeft(ax);
8525             }else if(ay !== undefined){
8526                 el.setTop(ay);
8527             }
8528             this.onPosition(ax, ay);
8529             this.fireEvent('move', this, ax, ay);
8530         }
8531         return this;
8532     },
8533
8534     /**
8535      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8536      * This method fires the move event.
8537      * @param {Number} x The new x position
8538      * @param {Number} y The new y position
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPagePosition : function(x, y){
8542         this.pageX = x;
8543         this.pageY = y;
8544         if(!this.boxReady){
8545             return;
8546         }
8547         if(x === undefined || y === undefined){ // cannot translate undefined points
8548             return;
8549         }
8550         var p = this.el.translatePoints(x, y);
8551         this.setPosition(p.left, p.top);
8552         return this;
8553     },
8554
8555     // private
8556     onRender : function(ct, position){
8557         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8558         if(this.resizeEl){
8559             this.resizeEl = Roo.get(this.resizeEl);
8560         }
8561         if(this.positionEl){
8562             this.positionEl = Roo.get(this.positionEl);
8563         }
8564     },
8565
8566     // private
8567     afterRender : function(){
8568         Roo.BoxComponent.superclass.afterRender.call(this);
8569         this.boxReady = true;
8570         this.setSize(this.width, this.height);
8571         if(this.x || this.y){
8572             this.setPosition(this.x, this.y);
8573         }
8574         if(this.pageX || this.pageY){
8575             this.setPagePosition(this.pageX, this.pageY);
8576         }
8577     },
8578
8579     /**
8580      * Force the component's size to recalculate based on the underlying element's current height and width.
8581      * @returns {Roo.BoxComponent} this
8582      */
8583     syncSize : function(){
8584         delete this.lastSize;
8585         this.setSize(this.el.getWidth(), this.el.getHeight());
8586         return this;
8587     },
8588
8589     /**
8590      * Called after the component is resized, this method is empty by default but can be implemented by any
8591      * subclass that needs to perform custom logic after a resize occurs.
8592      * @param {Number} adjWidth The box-adjusted width that was set
8593      * @param {Number} adjHeight The box-adjusted height that was set
8594      * @param {Number} rawWidth The width that was originally specified
8595      * @param {Number} rawHeight The height that was originally specified
8596      */
8597     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8598
8599     },
8600
8601     /**
8602      * Called after the component is moved, this method is empty by default but can be implemented by any
8603      * subclass that needs to perform custom logic after a move occurs.
8604      * @param {Number} x The new x position
8605      * @param {Number} y The new y position
8606      */
8607     onPosition : function(x, y){
8608
8609     },
8610
8611     // private
8612     adjustSize : function(w, h){
8613         if(this.autoWidth){
8614             w = 'auto';
8615         }
8616         if(this.autoHeight){
8617             h = 'auto';
8618         }
8619         return {width : w, height: h};
8620     },
8621
8622     // private
8623     adjustPosition : function(x, y){
8624         return {x : x, y: y};
8625     }
8626 });/*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636
8637
8638 /**
8639  * @class Roo.SplitBar
8640  * @extends Roo.util.Observable
8641  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8642  * <br><br>
8643  * Usage:
8644  * <pre><code>
8645 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8646                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8647 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8648 split.minSize = 100;
8649 split.maxSize = 600;
8650 split.animate = true;
8651 split.on('moved', splitterMoved);
8652 </code></pre>
8653  * @constructor
8654  * Create a new SplitBar
8655  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8656  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8657  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8658  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8659                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8660                         position of the SplitBar).
8661  */
8662 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8663     
8664     /** @private */
8665     this.el = Roo.get(dragElement, true);
8666     this.el.dom.unselectable = "on";
8667     /** @private */
8668     this.resizingEl = Roo.get(resizingElement, true);
8669
8670     /**
8671      * @private
8672      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8673      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8674      * @type Number
8675      */
8676     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8677     
8678     /**
8679      * The minimum size of the resizing element. (Defaults to 0)
8680      * @type Number
8681      */
8682     this.minSize = 0;
8683     
8684     /**
8685      * The maximum size of the resizing element. (Defaults to 2000)
8686      * @type Number
8687      */
8688     this.maxSize = 2000;
8689     
8690     /**
8691      * Whether to animate the transition to the new size
8692      * @type Boolean
8693      */
8694     this.animate = false;
8695     
8696     /**
8697      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8698      * @type Boolean
8699      */
8700     this.useShim = false;
8701     
8702     /** @private */
8703     this.shim = null;
8704     
8705     if(!existingProxy){
8706         /** @private */
8707         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8708     }else{
8709         this.proxy = Roo.get(existingProxy).dom;
8710     }
8711     /** @private */
8712     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8713     
8714     /** @private */
8715     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8716     
8717     /** @private */
8718     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8719     
8720     /** @private */
8721     this.dragSpecs = {};
8722     
8723     /**
8724      * @private The adapter to use to positon and resize elements
8725      */
8726     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8727     this.adapter.init(this);
8728     
8729     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8730         /** @private */
8731         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8732         this.el.addClass("x-splitbar-h");
8733     }else{
8734         /** @private */
8735         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8736         this.el.addClass("x-splitbar-v");
8737     }
8738     
8739     this.addEvents({
8740         /**
8741          * @event resize
8742          * Fires when the splitter is moved (alias for {@link #event-moved})
8743          * @param {Roo.SplitBar} this
8744          * @param {Number} newSize the new width or height
8745          */
8746         "resize" : true,
8747         /**
8748          * @event moved
8749          * Fires when the splitter is moved
8750          * @param {Roo.SplitBar} this
8751          * @param {Number} newSize the new width or height
8752          */
8753         "moved" : true,
8754         /**
8755          * @event beforeresize
8756          * Fires before the splitter is dragged
8757          * @param {Roo.SplitBar} this
8758          */
8759         "beforeresize" : true,
8760
8761         "beforeapply" : true
8762     });
8763
8764     Roo.util.Observable.call(this);
8765 };
8766
8767 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8768     onStartProxyDrag : function(x, y){
8769         this.fireEvent("beforeresize", this);
8770         if(!this.overlay){
8771             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8772             o.unselectable();
8773             o.enableDisplayMode("block");
8774             // all splitbars share the same overlay
8775             Roo.SplitBar.prototype.overlay = o;
8776         }
8777         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8778         this.overlay.show();
8779         Roo.get(this.proxy).setDisplayed("block");
8780         var size = this.adapter.getElementSize(this);
8781         this.activeMinSize = this.getMinimumSize();;
8782         this.activeMaxSize = this.getMaximumSize();;
8783         var c1 = size - this.activeMinSize;
8784         var c2 = Math.max(this.activeMaxSize - size, 0);
8785         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8786             this.dd.resetConstraints();
8787             this.dd.setXConstraint(
8788                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8789                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8790             );
8791             this.dd.setYConstraint(0, 0);
8792         }else{
8793             this.dd.resetConstraints();
8794             this.dd.setXConstraint(0, 0);
8795             this.dd.setYConstraint(
8796                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8797                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8798             );
8799          }
8800         this.dragSpecs.startSize = size;
8801         this.dragSpecs.startPoint = [x, y];
8802         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8803     },
8804     
8805     /** 
8806      * @private Called after the drag operation by the DDProxy
8807      */
8808     onEndProxyDrag : function(e){
8809         Roo.get(this.proxy).setDisplayed(false);
8810         var endPoint = Roo.lib.Event.getXY(e);
8811         if(this.overlay){
8812             this.overlay.hide();
8813         }
8814         var newSize;
8815         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8816             newSize = this.dragSpecs.startSize + 
8817                 (this.placement == Roo.SplitBar.LEFT ?
8818                     endPoint[0] - this.dragSpecs.startPoint[0] :
8819                     this.dragSpecs.startPoint[0] - endPoint[0]
8820                 );
8821         }else{
8822             newSize = this.dragSpecs.startSize + 
8823                 (this.placement == Roo.SplitBar.TOP ?
8824                     endPoint[1] - this.dragSpecs.startPoint[1] :
8825                     this.dragSpecs.startPoint[1] - endPoint[1]
8826                 );
8827         }
8828         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8829         if(newSize != this.dragSpecs.startSize){
8830             if(this.fireEvent('beforeapply', this, newSize) !== false){
8831                 this.adapter.setElementSize(this, newSize);
8832                 this.fireEvent("moved", this, newSize);
8833                 this.fireEvent("resize", this, newSize);
8834             }
8835         }
8836     },
8837     
8838     /**
8839      * Get the adapter this SplitBar uses
8840      * @return The adapter object
8841      */
8842     getAdapter : function(){
8843         return this.adapter;
8844     },
8845     
8846     /**
8847      * Set the adapter this SplitBar uses
8848      * @param {Object} adapter A SplitBar adapter object
8849      */
8850     setAdapter : function(adapter){
8851         this.adapter = adapter;
8852         this.adapter.init(this);
8853     },
8854     
8855     /**
8856      * Gets the minimum size for the resizing element
8857      * @return {Number} The minimum size
8858      */
8859     getMinimumSize : function(){
8860         return this.minSize;
8861     },
8862     
8863     /**
8864      * Sets the minimum size for the resizing element
8865      * @param {Number} minSize The minimum size
8866      */
8867     setMinimumSize : function(minSize){
8868         this.minSize = minSize;
8869     },
8870     
8871     /**
8872      * Gets the maximum size for the resizing element
8873      * @return {Number} The maximum size
8874      */
8875     getMaximumSize : function(){
8876         return this.maxSize;
8877     },
8878     
8879     /**
8880      * Sets the maximum size for the resizing element
8881      * @param {Number} maxSize The maximum size
8882      */
8883     setMaximumSize : function(maxSize){
8884         this.maxSize = maxSize;
8885     },
8886     
8887     /**
8888      * Sets the initialize size for the resizing element
8889      * @param {Number} size The initial size
8890      */
8891     setCurrentSize : function(size){
8892         var oldAnimate = this.animate;
8893         this.animate = false;
8894         this.adapter.setElementSize(this, size);
8895         this.animate = oldAnimate;
8896     },
8897     
8898     /**
8899      * Destroy this splitbar. 
8900      * @param {Boolean} removeEl True to remove the element
8901      */
8902     destroy : function(removeEl){
8903         if(this.shim){
8904             this.shim.remove();
8905         }
8906         this.dd.unreg();
8907         this.proxy.parentNode.removeChild(this.proxy);
8908         if(removeEl){
8909             this.el.remove();
8910         }
8911     }
8912 });
8913
8914 /**
8915  * @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.
8916  */
8917 Roo.SplitBar.createProxy = function(dir){
8918     var proxy = new Roo.Element(document.createElement("div"));
8919     proxy.unselectable();
8920     var cls = 'x-splitbar-proxy';
8921     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8922     document.body.appendChild(proxy.dom);
8923     return proxy.dom;
8924 };
8925
8926 /** 
8927  * @class Roo.SplitBar.BasicLayoutAdapter
8928  * Default Adapter. It assumes the splitter and resizing element are not positioned
8929  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8930  */
8931 Roo.SplitBar.BasicLayoutAdapter = function(){
8932 };
8933
8934 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8935     // do nothing for now
8936     init : function(s){
8937     
8938     },
8939     /**
8940      * Called before drag operations to get the current size of the resizing element. 
8941      * @param {Roo.SplitBar} s The SplitBar using this adapter
8942      */
8943      getElementSize : function(s){
8944         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8945             return s.resizingEl.getWidth();
8946         }else{
8947             return s.resizingEl.getHeight();
8948         }
8949     },
8950     
8951     /**
8952      * Called after drag operations to set the size of the resizing element.
8953      * @param {Roo.SplitBar} s The SplitBar using this adapter
8954      * @param {Number} newSize The new size to set
8955      * @param {Function} onComplete A function to be invoked when resizing is complete
8956      */
8957     setElementSize : function(s, newSize, onComplete){
8958         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8959             if(!s.animate){
8960                 s.resizingEl.setWidth(newSize);
8961                 if(onComplete){
8962                     onComplete(s, newSize);
8963                 }
8964             }else{
8965                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8966             }
8967         }else{
8968             
8969             if(!s.animate){
8970                 s.resizingEl.setHeight(newSize);
8971                 if(onComplete){
8972                     onComplete(s, newSize);
8973                 }
8974             }else{
8975                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8976             }
8977         }
8978     }
8979 };
8980
8981 /** 
8982  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8983  * @extends Roo.SplitBar.BasicLayoutAdapter
8984  * Adapter that  moves the splitter element to align with the resized sizing element. 
8985  * Used with an absolute positioned SplitBar.
8986  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8987  * document.body, make sure you assign an id to the body element.
8988  */
8989 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8990     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8991     this.container = Roo.get(container);
8992 };
8993
8994 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8995     init : function(s){
8996         this.basic.init(s);
8997     },
8998     
8999     getElementSize : function(s){
9000         return this.basic.getElementSize(s);
9001     },
9002     
9003     setElementSize : function(s, newSize, onComplete){
9004         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9005     },
9006     
9007     moveSplitter : function(s){
9008         var yes = Roo.SplitBar;
9009         switch(s.placement){
9010             case yes.LEFT:
9011                 s.el.setX(s.resizingEl.getRight());
9012                 break;
9013             case yes.RIGHT:
9014                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9015                 break;
9016             case yes.TOP:
9017                 s.el.setY(s.resizingEl.getBottom());
9018                 break;
9019             case yes.BOTTOM:
9020                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9021                 break;
9022         }
9023     }
9024 };
9025
9026 /**
9027  * Orientation constant - Create a vertical SplitBar
9028  * @static
9029  * @type Number
9030  */
9031 Roo.SplitBar.VERTICAL = 1;
9032
9033 /**
9034  * Orientation constant - Create a horizontal SplitBar
9035  * @static
9036  * @type Number
9037  */
9038 Roo.SplitBar.HORIZONTAL = 2;
9039
9040 /**
9041  * Placement constant - The resizing element is to the left of the splitter element
9042  * @static
9043  * @type Number
9044  */
9045 Roo.SplitBar.LEFT = 1;
9046
9047 /**
9048  * Placement constant - The resizing element is to the right of the splitter element
9049  * @static
9050  * @type Number
9051  */
9052 Roo.SplitBar.RIGHT = 2;
9053
9054 /**
9055  * Placement constant - The resizing element is positioned above the splitter element
9056  * @static
9057  * @type Number
9058  */
9059 Roo.SplitBar.TOP = 3;
9060
9061 /**
9062  * Placement constant - The resizing element is positioned under splitter element
9063  * @static
9064  * @type Number
9065  */
9066 Roo.SplitBar.BOTTOM = 4;
9067 /*
9068  * Based on:
9069  * Ext JS Library 1.1.1
9070  * Copyright(c) 2006-2007, Ext JS, LLC.
9071  *
9072  * Originally Released Under LGPL - original licence link has changed is not relivant.
9073  *
9074  * Fork - LGPL
9075  * <script type="text/javascript">
9076  */
9077
9078 /**
9079  * @class Roo.View
9080  * @extends Roo.util.Observable
9081  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9082  * This class also supports single and multi selection modes. <br>
9083  * Create a data model bound view:
9084  <pre><code>
9085  var store = new Roo.data.Store(...);
9086
9087  var view = new Roo.View({
9088     el : "my-element",
9089     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9090  
9091     singleSelect: true,
9092     selectedClass: "ydataview-selected",
9093     store: store
9094  });
9095
9096  // listen for node click?
9097  view.on("click", function(vw, index, node, e){
9098  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9099  });
9100
9101  // load XML data
9102  dataModel.load("foobar.xml");
9103  </code></pre>
9104  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9105  * <br><br>
9106  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9107  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9108  * 
9109  * Note: old style constructor is still suported (container, template, config)
9110  * 
9111  * @constructor
9112  * Create a new View
9113  * @param {Object} config The config object
9114  * 
9115  */
9116 Roo.View = function(config, depreciated_tpl, depreciated_config){
9117     
9118     if (typeof(depreciated_tpl) == 'undefined') {
9119         // new way.. - universal constructor.
9120         Roo.apply(this, config);
9121         this.el  = Roo.get(this.el);
9122     } else {
9123         // old format..
9124         this.el  = Roo.get(config);
9125         this.tpl = depreciated_tpl;
9126         Roo.apply(this, depreciated_config);
9127     }
9128      
9129     
9130     if(typeof(this.tpl) == "string"){
9131         this.tpl = new Roo.Template(this.tpl);
9132     } else {
9133         // support xtype ctors..
9134         this.tpl = new Roo.factory(this.tpl, Roo);
9135     }
9136     
9137     
9138     this.tpl.compile();
9139    
9140
9141      
9142     /** @private */
9143     this.addEvents({
9144     /**
9145      * @event beforeclick
9146      * Fires before a click is processed. Returns false to cancel the default action.
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         "beforeclick" : true,
9153     /**
9154      * @event click
9155      * Fires when a template node is clicked.
9156      * @param {Roo.View} this
9157      * @param {Number} index The index of the target node
9158      * @param {HTMLElement} node The target node
9159      * @param {Roo.EventObject} e The raw event object
9160      */
9161         "click" : true,
9162     /**
9163      * @event dblclick
9164      * Fires when a template node is double clicked.
9165      * @param {Roo.View} this
9166      * @param {Number} index The index of the target node
9167      * @param {HTMLElement} node The target node
9168      * @param {Roo.EventObject} e The raw event object
9169      */
9170         "dblclick" : true,
9171     /**
9172      * @event contextmenu
9173      * Fires when a template node is right clicked.
9174      * @param {Roo.View} this
9175      * @param {Number} index The index of the target node
9176      * @param {HTMLElement} node The target node
9177      * @param {Roo.EventObject} e The raw event object
9178      */
9179         "contextmenu" : true,
9180     /**
9181      * @event selectionchange
9182      * Fires when the selected nodes change.
9183      * @param {Roo.View} this
9184      * @param {Array} selections Array of the selected nodes
9185      */
9186         "selectionchange" : true,
9187
9188     /**
9189      * @event beforeselect
9190      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9191      * @param {Roo.View} this
9192      * @param {HTMLElement} node The node to be selected
9193      * @param {Array} selections Array of currently selected nodes
9194      */
9195         "beforeselect" : true
9196     });
9197
9198     this.el.on({
9199         "click": this.onClick,
9200         "dblclick": this.onDblClick,
9201         "contextmenu": this.onContextMenu,
9202         scope:this
9203     });
9204
9205     this.selections = [];
9206     this.nodes = [];
9207     this.cmp = new Roo.CompositeElementLite([]);
9208     if(this.store){
9209         this.store = Roo.factory(this.store, Roo.data);
9210         this.setStore(this.store, true);
9211     }
9212     Roo.View.superclass.constructor.call(this);
9213 };
9214
9215 Roo.extend(Roo.View, Roo.util.Observable, {
9216     
9217      /**
9218      * @cfg {Roo.data.Store} store Data store to load data from.
9219      */
9220     store : false,
9221     
9222     /**
9223      * @cfg {String|Roo.Element} el The container element.
9224      */
9225     el : '',
9226     
9227     /**
9228      * @cfg {String|Roo.Template} tpl The template used by this View 
9229      */
9230     tpl : false,
9231     
9232     /**
9233      * @cfg {String} selectedClass The css class to add to selected nodes
9234      */
9235     selectedClass : "x-view-selected",
9236      /**
9237      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9238      */
9239     emptyText : "",
9240     /**
9241      * @cfg {Boolean} multiSelect Allow multiple selection
9242      */
9243     
9244     multiSelect : false,
9245     /**
9246      * @cfg {Boolean} singleSelect Allow single selection
9247      */
9248     singleSelect:  false,
9249     
9250     /**
9251      * Returns the element this view is bound to.
9252      * @return {Roo.Element}
9253      */
9254     getEl : function(){
9255         return this.el;
9256     },
9257
9258     /**
9259      * Refreshes the view.
9260      */
9261     refresh : function(){
9262         var t = this.tpl;
9263         this.clearSelections();
9264         this.el.update("");
9265         var html = [];
9266         var records = this.store.getRange();
9267         if(records.length < 1){
9268             this.el.update(this.emptyText);
9269             return;
9270         }
9271         for(var i = 0, len = records.length; i < len; i++){
9272             var data = this.prepareData(records[i].data, i, records[i]);
9273             html[html.length] = t.apply(data);
9274         }
9275         this.el.update(html.join(""));
9276         this.nodes = this.el.dom.childNodes;
9277         this.updateIndexes(0);
9278     },
9279
9280     /**
9281      * Function to override to reformat the data that is sent to
9282      * the template for each node.
9283      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9284      * a JSON object for an UpdateManager bound view).
9285      */
9286     prepareData : function(data){
9287         return data;
9288     },
9289
9290     onUpdate : function(ds, record){
9291         this.clearSelections();
9292         var index = this.store.indexOf(record);
9293         var n = this.nodes[index];
9294         this.tpl.insertBefore(n, this.prepareData(record.data));
9295         n.parentNode.removeChild(n);
9296         this.updateIndexes(index, index);
9297     },
9298
9299     onAdd : function(ds, records, index){
9300         this.clearSelections();
9301         if(this.nodes.length == 0){
9302             this.refresh();
9303             return;
9304         }
9305         var n = this.nodes[index];
9306         for(var i = 0, len = records.length; i < len; i++){
9307             var d = this.prepareData(records[i].data);
9308             if(n){
9309                 this.tpl.insertBefore(n, d);
9310             }else{
9311                 this.tpl.append(this.el, d);
9312             }
9313         }
9314         this.updateIndexes(index);
9315     },
9316
9317     onRemove : function(ds, record, index){
9318         this.clearSelections();
9319         this.el.dom.removeChild(this.nodes[index]);
9320         this.updateIndexes(index);
9321     },
9322
9323     /**
9324      * Refresh an individual node.
9325      * @param {Number} index
9326      */
9327     refreshNode : function(index){
9328         this.onUpdate(this.store, this.store.getAt(index));
9329     },
9330
9331     updateIndexes : function(startIndex, endIndex){
9332         var ns = this.nodes;
9333         startIndex = startIndex || 0;
9334         endIndex = endIndex || ns.length - 1;
9335         for(var i = startIndex; i <= endIndex; i++){
9336             ns[i].nodeIndex = i;
9337         }
9338     },
9339
9340     /**
9341      * Changes the data store this view uses and refresh the view.
9342      * @param {Store} store
9343      */
9344     setStore : function(store, initial){
9345         if(!initial && this.store){
9346             this.store.un("datachanged", this.refresh);
9347             this.store.un("add", this.onAdd);
9348             this.store.un("remove", this.onRemove);
9349             this.store.un("update", this.onUpdate);
9350             this.store.un("clear", this.refresh);
9351         }
9352         if(store){
9353           
9354             store.on("datachanged", this.refresh, this);
9355             store.on("add", this.onAdd, this);
9356             store.on("remove", this.onRemove, this);
9357             store.on("update", this.onUpdate, this);
9358             store.on("clear", this.refresh, this);
9359         }
9360         
9361         if(store){
9362             this.refresh();
9363         }
9364     },
9365
9366     /**
9367      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9368      * @param {HTMLElement} node
9369      * @return {HTMLElement} The template node
9370      */
9371     findItemFromChild : function(node){
9372         var el = this.el.dom;
9373         if(!node || node.parentNode == el){
9374                     return node;
9375             }
9376             var p = node.parentNode;
9377             while(p && p != el){
9378             if(p.parentNode == el){
9379                 return p;
9380             }
9381             p = p.parentNode;
9382         }
9383             return null;
9384     },
9385
9386     /** @ignore */
9387     onClick : function(e){
9388         var item = this.findItemFromChild(e.getTarget());
9389         if(item){
9390             var index = this.indexOf(item);
9391             if(this.onItemClick(item, index, e) !== false){
9392                 this.fireEvent("click", this, index, item, e);
9393             }
9394         }else{
9395             this.clearSelections();
9396         }
9397     },
9398
9399     /** @ignore */
9400     onContextMenu : function(e){
9401         var item = this.findItemFromChild(e.getTarget());
9402         if(item){
9403             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9404         }
9405     },
9406
9407     /** @ignore */
9408     onDblClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9412         }
9413     },
9414
9415     onItemClick : function(item, index, e){
9416         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9417             return false;
9418         }
9419         if(this.multiSelect || this.singleSelect){
9420             if(this.multiSelect && e.shiftKey && this.lastSelection){
9421                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9422             }else{
9423                 this.select(item, this.multiSelect && e.ctrlKey);
9424                 this.lastSelection = item;
9425             }
9426             e.preventDefault();
9427         }
9428         return true;
9429     },
9430
9431     /**
9432      * Get the number of selected nodes.
9433      * @return {Number}
9434      */
9435     getSelectionCount : function(){
9436         return this.selections.length;
9437     },
9438
9439     /**
9440      * Get the currently selected nodes.
9441      * @return {Array} An array of HTMLElements
9442      */
9443     getSelectedNodes : function(){
9444         return this.selections;
9445     },
9446
9447     /**
9448      * Get the indexes of the selected nodes.
9449      * @return {Array}
9450      */
9451     getSelectedIndexes : function(){
9452         var indexes = [], s = this.selections;
9453         for(var i = 0, len = s.length; i < len; i++){
9454             indexes.push(s[i].nodeIndex);
9455         }
9456         return indexes;
9457     },
9458
9459     /**
9460      * Clear all selections
9461      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9462      */
9463     clearSelections : function(suppressEvent){
9464         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9465             this.cmp.elements = this.selections;
9466             this.cmp.removeClass(this.selectedClass);
9467             this.selections = [];
9468             if(!suppressEvent){
9469                 this.fireEvent("selectionchange", this, this.selections);
9470             }
9471         }
9472     },
9473
9474     /**
9475      * Returns true if the passed node is selected
9476      * @param {HTMLElement/Number} node The node or node index
9477      * @return {Boolean}
9478      */
9479     isSelected : function(node){
9480         var s = this.selections;
9481         if(s.length < 1){
9482             return false;
9483         }
9484         node = this.getNode(node);
9485         return s.indexOf(node) !== -1;
9486     },
9487
9488     /**
9489      * Selects nodes.
9490      * @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
9491      * @param {Boolean} keepExisting (optional) true to keep existing selections
9492      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9493      */
9494     select : function(nodeInfo, keepExisting, suppressEvent){
9495         if(nodeInfo instanceof Array){
9496             if(!keepExisting){
9497                 this.clearSelections(true);
9498             }
9499             for(var i = 0, len = nodeInfo.length; i < len; i++){
9500                 this.select(nodeInfo[i], true, true);
9501             }
9502         } else{
9503             var node = this.getNode(nodeInfo);
9504             if(node && !this.isSelected(node)){
9505                 if(!keepExisting){
9506                     this.clearSelections(true);
9507                 }
9508                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9509                     Roo.fly(node).addClass(this.selectedClass);
9510                     this.selections.push(node);
9511                     if(!suppressEvent){
9512                         this.fireEvent("selectionchange", this, this.selections);
9513                     }
9514                 }
9515             }
9516         }
9517     },
9518
9519     /**
9520      * Gets a template node.
9521      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9522      * @return {HTMLElement} The node or null if it wasn't found
9523      */
9524     getNode : function(nodeInfo){
9525         if(typeof nodeInfo == "string"){
9526             return document.getElementById(nodeInfo);
9527         }else if(typeof nodeInfo == "number"){
9528             return this.nodes[nodeInfo];
9529         }
9530         return nodeInfo;
9531     },
9532
9533     /**
9534      * Gets a range template nodes.
9535      * @param {Number} startIndex
9536      * @param {Number} endIndex
9537      * @return {Array} An array of nodes
9538      */
9539     getNodes : function(start, end){
9540         var ns = this.nodes;
9541         start = start || 0;
9542         end = typeof end == "undefined" ? ns.length - 1 : end;
9543         var nodes = [];
9544         if(start <= end){
9545             for(var i = start; i <= end; i++){
9546                 nodes.push(ns[i]);
9547             }
9548         } else{
9549             for(var i = start; i >= end; i--){
9550                 nodes.push(ns[i]);
9551             }
9552         }
9553         return nodes;
9554     },
9555
9556     /**
9557      * Finds the index of the passed node
9558      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9559      * @return {Number} The index of the node or -1
9560      */
9561     indexOf : function(node){
9562         node = this.getNode(node);
9563         if(typeof node.nodeIndex == "number"){
9564             return node.nodeIndex;
9565         }
9566         var ns = this.nodes;
9567         for(var i = 0, len = ns.length; i < len; i++){
9568             if(ns[i] == node){
9569                 return i;
9570             }
9571         }
9572         return -1;
9573     }
9574 });
9575 /*
9576  * Based on:
9577  * Ext JS Library 1.1.1
9578  * Copyright(c) 2006-2007, Ext JS, LLC.
9579  *
9580  * Originally Released Under LGPL - original licence link has changed is not relivant.
9581  *
9582  * Fork - LGPL
9583  * <script type="text/javascript">
9584  */
9585
9586 /**
9587  * @class Roo.JsonView
9588  * @extends Roo.View
9589  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9590 <pre><code>
9591 var view = new Roo.JsonView({
9592     container: "my-element",
9593     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9594     multiSelect: true, 
9595     jsonRoot: "data" 
9596 });
9597
9598 // listen for node click?
9599 view.on("click", function(vw, index, node, e){
9600     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9601 });
9602
9603 // direct load of JSON data
9604 view.load("foobar.php");
9605
9606 // Example from my blog list
9607 var tpl = new Roo.Template(
9608     '&lt;div class="entry"&gt;' +
9609     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9610     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9611     "&lt;/div&gt;&lt;hr /&gt;"
9612 );
9613
9614 var moreView = new Roo.JsonView({
9615     container :  "entry-list", 
9616     template : tpl,
9617     jsonRoot: "posts"
9618 });
9619 moreView.on("beforerender", this.sortEntries, this);
9620 moreView.load({
9621     url: "/blog/get-posts.php",
9622     params: "allposts=true",
9623     text: "Loading Blog Entries..."
9624 });
9625 </code></pre>
9626
9627 * Note: old code is supported with arguments : (container, template, config)
9628
9629
9630  * @constructor
9631  * Create a new JsonView
9632  * 
9633  * @param {Object} config The config object
9634  * 
9635  */
9636 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9637     
9638     
9639     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9640
9641     var um = this.el.getUpdateManager();
9642     um.setRenderer(this);
9643     um.on("update", this.onLoad, this);
9644     um.on("failure", this.onLoadException, this);
9645
9646     /**
9647      * @event beforerender
9648      * Fires before rendering of the downloaded JSON data.
9649      * @param {Roo.JsonView} this
9650      * @param {Object} data The JSON data loaded
9651      */
9652     /**
9653      * @event load
9654      * Fires when data is loaded.
9655      * @param {Roo.JsonView} this
9656      * @param {Object} data The JSON data loaded
9657      * @param {Object} response The raw Connect response object
9658      */
9659     /**
9660      * @event loadexception
9661      * Fires when loading fails.
9662      * @param {Roo.JsonView} this
9663      * @param {Object} response The raw Connect response object
9664      */
9665     this.addEvents({
9666         'beforerender' : true,
9667         'load' : true,
9668         'loadexception' : true
9669     });
9670 };
9671 Roo.extend(Roo.JsonView, Roo.View, {
9672     /**
9673      * @type {String} The root property in the loaded JSON object that contains the data
9674      */
9675     jsonRoot : "",
9676
9677     /**
9678      * Refreshes the view.
9679      */
9680     refresh : function(){
9681         this.clearSelections();
9682         this.el.update("");
9683         var html = [];
9684         var o = this.jsonData;
9685         if(o && o.length > 0){
9686             for(var i = 0, len = o.length; i < len; i++){
9687                 var data = this.prepareData(o[i], i, o);
9688                 html[html.length] = this.tpl.apply(data);
9689             }
9690         }else{
9691             html.push(this.emptyText);
9692         }
9693         this.el.update(html.join(""));
9694         this.nodes = this.el.dom.childNodes;
9695         this.updateIndexes(0);
9696     },
9697
9698     /**
9699      * 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.
9700      * @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:
9701      <pre><code>
9702      view.load({
9703          url: "your-url.php",
9704          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9705          callback: yourFunction,
9706          scope: yourObject, //(optional scope)
9707          discardUrl: false,
9708          nocache: false,
9709          text: "Loading...",
9710          timeout: 30,
9711          scripts: false
9712      });
9713      </code></pre>
9714      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9715      * 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.
9716      * @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}
9717      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9718      * @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.
9719      */
9720     load : function(){
9721         var um = this.el.getUpdateManager();
9722         um.update.apply(um, arguments);
9723     },
9724
9725     render : function(el, response){
9726         this.clearSelections();
9727         this.el.update("");
9728         var o;
9729         try{
9730             o = Roo.util.JSON.decode(response.responseText);
9731             if(this.jsonRoot){
9732                 
9733                 o = o[this.jsonRoot];
9734             }
9735         } catch(e){
9736         }
9737         /**
9738          * The current JSON data or null
9739          */
9740         this.jsonData = o;
9741         this.beforeRender();
9742         this.refresh();
9743     },
9744
9745 /**
9746  * Get the number of records in the current JSON dataset
9747  * @return {Number}
9748  */
9749     getCount : function(){
9750         return this.jsonData ? this.jsonData.length : 0;
9751     },
9752
9753 /**
9754  * Returns the JSON object for the specified node(s)
9755  * @param {HTMLElement/Array} node The node or an array of nodes
9756  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9757  * you get the JSON object for the node
9758  */
9759     getNodeData : function(node){
9760         if(node instanceof Array){
9761             var data = [];
9762             for(var i = 0, len = node.length; i < len; i++){
9763                 data.push(this.getNodeData(node[i]));
9764             }
9765             return data;
9766         }
9767         return this.jsonData[this.indexOf(node)] || null;
9768     },
9769
9770     beforeRender : function(){
9771         this.snapshot = this.jsonData;
9772         if(this.sortInfo){
9773             this.sort.apply(this, this.sortInfo);
9774         }
9775         this.fireEvent("beforerender", this, this.jsonData);
9776     },
9777
9778     onLoad : function(el, o){
9779         this.fireEvent("load", this, this.jsonData, o);
9780     },
9781
9782     onLoadException : function(el, o){
9783         this.fireEvent("loadexception", this, o);
9784     },
9785
9786 /**
9787  * Filter the data by a specific property.
9788  * @param {String} property A property on your JSON objects
9789  * @param {String/RegExp} value Either string that the property values
9790  * should start with, or a RegExp to test against the property
9791  */
9792     filter : function(property, value){
9793         if(this.jsonData){
9794             var data = [];
9795             var ss = this.snapshot;
9796             if(typeof value == "string"){
9797                 var vlen = value.length;
9798                 if(vlen == 0){
9799                     this.clearFilter();
9800                     return;
9801                 }
9802                 value = value.toLowerCase();
9803                 for(var i = 0, len = ss.length; i < len; i++){
9804                     var o = ss[i];
9805                     if(o[property].substr(0, vlen).toLowerCase() == value){
9806                         data.push(o);
9807                     }
9808                 }
9809             } else if(value.exec){ // regex?
9810                 for(var i = 0, len = ss.length; i < len; i++){
9811                     var o = ss[i];
9812                     if(value.test(o[property])){
9813                         data.push(o);
9814                     }
9815                 }
9816             } else{
9817                 return;
9818             }
9819             this.jsonData = data;
9820             this.refresh();
9821         }
9822     },
9823
9824 /**
9825  * Filter by a function. The passed function will be called with each
9826  * object in the current dataset. If the function returns true the value is kept,
9827  * otherwise it is filtered.
9828  * @param {Function} fn
9829  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9830  */
9831     filterBy : function(fn, scope){
9832         if(this.jsonData){
9833             var data = [];
9834             var ss = this.snapshot;
9835             for(var i = 0, len = ss.length; i < len; i++){
9836                 var o = ss[i];
9837                 if(fn.call(scope || this, o)){
9838                     data.push(o);
9839                 }
9840             }
9841             this.jsonData = data;
9842             this.refresh();
9843         }
9844     },
9845
9846 /**
9847  * Clears the current filter.
9848  */
9849     clearFilter : function(){
9850         if(this.snapshot && this.jsonData != this.snapshot){
9851             this.jsonData = this.snapshot;
9852             this.refresh();
9853         }
9854     },
9855
9856
9857 /**
9858  * Sorts the data for this view and refreshes it.
9859  * @param {String} property A property on your JSON objects to sort on
9860  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9861  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9862  */
9863     sort : function(property, dir, sortType){
9864         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9865         if(this.jsonData){
9866             var p = property;
9867             var dsc = dir && dir.toLowerCase() == "desc";
9868             var f = function(o1, o2){
9869                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9870                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9871                 ;
9872                 if(v1 < v2){
9873                     return dsc ? +1 : -1;
9874                 } else if(v1 > v2){
9875                     return dsc ? -1 : +1;
9876                 } else{
9877                     return 0;
9878                 }
9879             };
9880             this.jsonData.sort(f);
9881             this.refresh();
9882             if(this.jsonData != this.snapshot){
9883                 this.snapshot.sort(f);
9884             }
9885         }
9886     }
9887 });/*
9888  * Based on:
9889  * Ext JS Library 1.1.1
9890  * Copyright(c) 2006-2007, Ext JS, LLC.
9891  *
9892  * Originally Released Under LGPL - original licence link has changed is not relivant.
9893  *
9894  * Fork - LGPL
9895  * <script type="text/javascript">
9896  */
9897  
9898
9899 /**
9900  * @class Roo.ColorPalette
9901  * @extends Roo.Component
9902  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9903  * Here's an example of typical usage:
9904  * <pre><code>
9905 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9906 cp.render('my-div');
9907
9908 cp.on('select', function(palette, selColor){
9909     // do something with selColor
9910 });
9911 </code></pre>
9912  * @constructor
9913  * Create a new ColorPalette
9914  * @param {Object} config The config object
9915  */
9916 Roo.ColorPalette = function(config){
9917     Roo.ColorPalette.superclass.constructor.call(this, config);
9918     this.addEvents({
9919         /**
9920              * @event select
9921              * Fires when a color is selected
9922              * @param {ColorPalette} this
9923              * @param {String} color The 6-digit color hex code (without the # symbol)
9924              */
9925         select: true
9926     });
9927
9928     if(this.handler){
9929         this.on("select", this.handler, this.scope, true);
9930     }
9931 };
9932 Roo.extend(Roo.ColorPalette, Roo.Component, {
9933     /**
9934      * @cfg {String} itemCls
9935      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9936      */
9937     itemCls : "x-color-palette",
9938     /**
9939      * @cfg {String} value
9940      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9941      * the hex codes are case-sensitive.
9942      */
9943     value : null,
9944     clickEvent:'click',
9945     // private
9946     ctype: "Roo.ColorPalette",
9947
9948     /**
9949      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9950      */
9951     allowReselect : false,
9952
9953     /**
9954      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9955      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9956      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9957      * of colors with the width setting until the box is symmetrical.</p>
9958      * <p>You can override individual colors if needed:</p>
9959      * <pre><code>
9960 var cp = new Roo.ColorPalette();
9961 cp.colors[0] = "FF0000";  // change the first box to red
9962 </code></pre>
9963
9964 Or you can provide a custom array of your own for complete control:
9965 <pre><code>
9966 var cp = new Roo.ColorPalette();
9967 cp.colors = ["000000", "993300", "333300"];
9968 </code></pre>
9969      * @type Array
9970      */
9971     colors : [
9972         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9973         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9974         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9975         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9976         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9977     ],
9978
9979     // private
9980     onRender : function(container, position){
9981         var t = new Roo.MasterTemplate(
9982             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9983         );
9984         var c = this.colors;
9985         for(var i = 0, len = c.length; i < len; i++){
9986             t.add([c[i]]);
9987         }
9988         var el = document.createElement("div");
9989         el.className = this.itemCls;
9990         t.overwrite(el);
9991         container.dom.insertBefore(el, position);
9992         this.el = Roo.get(el);
9993         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9994         if(this.clickEvent != 'click'){
9995             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9996         }
9997     },
9998
9999     // private
10000     afterRender : function(){
10001         Roo.ColorPalette.superclass.afterRender.call(this);
10002         if(this.value){
10003             var s = this.value;
10004             this.value = null;
10005             this.select(s);
10006         }
10007     },
10008
10009     // private
10010     handleClick : function(e, t){
10011         e.preventDefault();
10012         if(!this.disabled){
10013             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10014             this.select(c.toUpperCase());
10015         }
10016     },
10017
10018     /**
10019      * Selects the specified color in the palette (fires the select event)
10020      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10021      */
10022     select : function(color){
10023         color = color.replace("#", "");
10024         if(color != this.value || this.allowReselect){
10025             var el = this.el;
10026             if(this.value){
10027                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10028             }
10029             el.child("a.color-"+color).addClass("x-color-palette-sel");
10030             this.value = color;
10031             this.fireEvent("select", this, color);
10032         }
10033     }
10034 });/*
10035  * Based on:
10036  * Ext JS Library 1.1.1
10037  * Copyright(c) 2006-2007, Ext JS, LLC.
10038  *
10039  * Originally Released Under LGPL - original licence link has changed is not relivant.
10040  *
10041  * Fork - LGPL
10042  * <script type="text/javascript">
10043  */
10044  
10045 /**
10046  * @class Roo.DatePicker
10047  * @extends Roo.Component
10048  * Simple date picker class.
10049  * @constructor
10050  * Create a new DatePicker
10051  * @param {Object} config The config object
10052  */
10053 Roo.DatePicker = function(config){
10054     Roo.DatePicker.superclass.constructor.call(this, config);
10055
10056     this.value = config && config.value ?
10057                  config.value.clearTime() : new Date().clearTime();
10058
10059     this.addEvents({
10060         /**
10061              * @event select
10062              * Fires when a date is selected
10063              * @param {DatePicker} this
10064              * @param {Date} date The selected date
10065              */
10066         select: true
10067     });
10068
10069     if(this.handler){
10070         this.on("select", this.handler,  this.scope || this);
10071     }
10072     // build the disabledDatesRE
10073     if(!this.disabledDatesRE && this.disabledDates){
10074         var dd = this.disabledDates;
10075         var re = "(?:";
10076         for(var i = 0; i < dd.length; i++){
10077             re += dd[i];
10078             if(i != dd.length-1) re += "|";
10079         }
10080         this.disabledDatesRE = new RegExp(re + ")");
10081     }
10082 };
10083
10084 Roo.extend(Roo.DatePicker, Roo.Component, {
10085     /**
10086      * @cfg {String} todayText
10087      * The text to display on the button that selects the current date (defaults to "Today")
10088      */
10089     todayText : "Today",
10090     /**
10091      * @cfg {String} okText
10092      * The text to display on the ok button
10093      */
10094     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10095     /**
10096      * @cfg {String} cancelText
10097      * The text to display on the cancel button
10098      */
10099     cancelText : "Cancel",
10100     /**
10101      * @cfg {String} todayTip
10102      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10103      */
10104     todayTip : "{0} (Spacebar)",
10105     /**
10106      * @cfg {Date} minDate
10107      * Minimum allowable date (JavaScript date object, defaults to null)
10108      */
10109     minDate : null,
10110     /**
10111      * @cfg {Date} maxDate
10112      * Maximum allowable date (JavaScript date object, defaults to null)
10113      */
10114     maxDate : null,
10115     /**
10116      * @cfg {String} minText
10117      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10118      */
10119     minText : "This date is before the minimum date",
10120     /**
10121      * @cfg {String} maxText
10122      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10123      */
10124     maxText : "This date is after the maximum date",
10125     /**
10126      * @cfg {String} format
10127      * The default date format string which can be overriden for localization support.  The format must be
10128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10129      */
10130     format : "m/d/y",
10131     /**
10132      * @cfg {Array} disabledDays
10133      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10134      */
10135     disabledDays : null,
10136     /**
10137      * @cfg {String} disabledDaysText
10138      * The tooltip to display when the date falls on a disabled day (defaults to "")
10139      */
10140     disabledDaysText : "",
10141     /**
10142      * @cfg {RegExp} disabledDatesRE
10143      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10144      */
10145     disabledDatesRE : null,
10146     /**
10147      * @cfg {String} disabledDatesText
10148      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10149      */
10150     disabledDatesText : "",
10151     /**
10152      * @cfg {Boolean} constrainToViewport
10153      * True to constrain the date picker to the viewport (defaults to true)
10154      */
10155     constrainToViewport : true,
10156     /**
10157      * @cfg {Array} monthNames
10158      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10159      */
10160     monthNames : Date.monthNames,
10161     /**
10162      * @cfg {Array} dayNames
10163      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10164      */
10165     dayNames : Date.dayNames,
10166     /**
10167      * @cfg {String} nextText
10168      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10169      */
10170     nextText: 'Next Month (Control+Right)',
10171     /**
10172      * @cfg {String} prevText
10173      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10174      */
10175     prevText: 'Previous Month (Control+Left)',
10176     /**
10177      * @cfg {String} monthYearText
10178      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10179      */
10180     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10181     /**
10182      * @cfg {Number} startDay
10183      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10184      */
10185     startDay : 0,
10186     /**
10187      * @cfg {Bool} showClear
10188      * Show a clear button (usefull for date form elements that can be blank.)
10189      */
10190     
10191     showClear: false,
10192     
10193     /**
10194      * Sets the value of the date field
10195      * @param {Date} value The date to set
10196      */
10197     setValue : function(value){
10198         var old = this.value;
10199         this.value = value.clearTime(true);
10200         if(this.el){
10201             this.update(this.value);
10202         }
10203     },
10204
10205     /**
10206      * Gets the current selected value of the date field
10207      * @return {Date} The selected date
10208      */
10209     getValue : function(){
10210         return this.value;
10211     },
10212
10213     // private
10214     focus : function(){
10215         if(this.el){
10216             this.update(this.activeDate);
10217         }
10218     },
10219
10220     // private
10221     onRender : function(container, position){
10222         var m = [
10223              '<table cellspacing="0">',
10224                 '<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>',
10225                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10226         var dn = this.dayNames;
10227         for(var i = 0; i < 7; i++){
10228             var d = this.startDay+i;
10229             if(d > 6){
10230                 d = d-7;
10231             }
10232             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10233         }
10234         m[m.length] = "</tr></thead><tbody><tr>";
10235         for(var i = 0; i < 42; i++) {
10236             if(i % 7 == 0 && i != 0){
10237                 m[m.length] = "</tr><tr>";
10238             }
10239             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10240         }
10241         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10242             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10243
10244         var el = document.createElement("div");
10245         el.className = "x-date-picker";
10246         el.innerHTML = m.join("");
10247
10248         container.dom.insertBefore(el, position);
10249
10250         this.el = Roo.get(el);
10251         this.eventEl = Roo.get(el.firstChild);
10252
10253         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10254             handler: this.showPrevMonth,
10255             scope: this,
10256             preventDefault:true,
10257             stopDefault:true
10258         });
10259
10260         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10261             handler: this.showNextMonth,
10262             scope: this,
10263             preventDefault:true,
10264             stopDefault:true
10265         });
10266
10267         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10268
10269         this.monthPicker = this.el.down('div.x-date-mp');
10270         this.monthPicker.enableDisplayMode('block');
10271         
10272         var kn = new Roo.KeyNav(this.eventEl, {
10273             "left" : function(e){
10274                 e.ctrlKey ?
10275                     this.showPrevMonth() :
10276                     this.update(this.activeDate.add("d", -1));
10277             },
10278
10279             "right" : function(e){
10280                 e.ctrlKey ?
10281                     this.showNextMonth() :
10282                     this.update(this.activeDate.add("d", 1));
10283             },
10284
10285             "up" : function(e){
10286                 e.ctrlKey ?
10287                     this.showNextYear() :
10288                     this.update(this.activeDate.add("d", -7));
10289             },
10290
10291             "down" : function(e){
10292                 e.ctrlKey ?
10293                     this.showPrevYear() :
10294                     this.update(this.activeDate.add("d", 7));
10295             },
10296
10297             "pageUp" : function(e){
10298                 this.showNextMonth();
10299             },
10300
10301             "pageDown" : function(e){
10302                 this.showPrevMonth();
10303             },
10304
10305             "enter" : function(e){
10306                 e.stopPropagation();
10307                 return true;
10308             },
10309
10310             scope : this
10311         });
10312
10313         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10314
10315         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10316
10317         this.el.unselectable();
10318         
10319         this.cells = this.el.select("table.x-date-inner tbody td");
10320         this.textNodes = this.el.query("table.x-date-inner tbody span");
10321
10322         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10323             text: "&#160;",
10324             tooltip: this.monthYearText
10325         });
10326
10327         this.mbtn.on('click', this.showMonthPicker, this);
10328         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10329
10330
10331         var today = (new Date()).dateFormat(this.format);
10332         
10333         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10334         if (this.showClear) {
10335             baseTb.add( new Roo.Toolbar.Fill());
10336         }
10337         baseTb.add({
10338             text: String.format(this.todayText, today),
10339             tooltip: String.format(this.todayTip, today),
10340             handler: this.selectToday,
10341             scope: this
10342         });
10343         
10344         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10345             
10346         //});
10347         if (this.showClear) {
10348             
10349             baseTb.add( new Roo.Toolbar.Fill());
10350             baseTb.add({
10351                 text: '&#160;',
10352                 cls: 'x-btn-icon x-btn-clear',
10353                 handler: function() {
10354                     //this.value = '';
10355                     this.fireEvent("select", this, '');
10356                 },
10357                 scope: this
10358             });
10359         }
10360         
10361         
10362         if(Roo.isIE){
10363             this.el.repaint();
10364         }
10365         this.update(this.value);
10366     },
10367
10368     createMonthPicker : function(){
10369         if(!this.monthPicker.dom.firstChild){
10370             var buf = ['<table border="0" cellspacing="0">'];
10371             for(var i = 0; i < 6; i++){
10372                 buf.push(
10373                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10374                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10375                     i == 0 ?
10376                     '<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>' :
10377                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10378                 );
10379             }
10380             buf.push(
10381                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10382                     this.okText,
10383                     '</button><button type="button" class="x-date-mp-cancel">',
10384                     this.cancelText,
10385                     '</button></td></tr>',
10386                 '</table>'
10387             );
10388             this.monthPicker.update(buf.join(''));
10389             this.monthPicker.on('click', this.onMonthClick, this);
10390             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10391
10392             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10393             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10394
10395             this.mpMonths.each(function(m, a, i){
10396                 i += 1;
10397                 if((i%2) == 0){
10398                     m.dom.xmonth = 5 + Math.round(i * .5);
10399                 }else{
10400                     m.dom.xmonth = Math.round((i-1) * .5);
10401                 }
10402             });
10403         }
10404     },
10405
10406     showMonthPicker : function(){
10407         this.createMonthPicker();
10408         var size = this.el.getSize();
10409         this.monthPicker.setSize(size);
10410         this.monthPicker.child('table').setSize(size);
10411
10412         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10413         this.updateMPMonth(this.mpSelMonth);
10414         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10415         this.updateMPYear(this.mpSelYear);
10416
10417         this.monthPicker.slideIn('t', {duration:.2});
10418     },
10419
10420     updateMPYear : function(y){
10421         this.mpyear = y;
10422         var ys = this.mpYears.elements;
10423         for(var i = 1; i <= 10; i++){
10424             var td = ys[i-1], y2;
10425             if((i%2) == 0){
10426                 y2 = y + Math.round(i * .5);
10427                 td.firstChild.innerHTML = y2;
10428                 td.xyear = y2;
10429             }else{
10430                 y2 = y - (5-Math.round(i * .5));
10431                 td.firstChild.innerHTML = y2;
10432                 td.xyear = y2;
10433             }
10434             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10435         }
10436     },
10437
10438     updateMPMonth : function(sm){
10439         this.mpMonths.each(function(m, a, i){
10440             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10441         });
10442     },
10443
10444     selectMPMonth: function(m){
10445         
10446     },
10447
10448     onMonthClick : function(e, t){
10449         e.stopEvent();
10450         var el = new Roo.Element(t), pn;
10451         if(el.is('button.x-date-mp-cancel')){
10452             this.hideMonthPicker();
10453         }
10454         else if(el.is('button.x-date-mp-ok')){
10455             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10456             this.hideMonthPicker();
10457         }
10458         else if(pn = el.up('td.x-date-mp-month', 2)){
10459             this.mpMonths.removeClass('x-date-mp-sel');
10460             pn.addClass('x-date-mp-sel');
10461             this.mpSelMonth = pn.dom.xmonth;
10462         }
10463         else if(pn = el.up('td.x-date-mp-year', 2)){
10464             this.mpYears.removeClass('x-date-mp-sel');
10465             pn.addClass('x-date-mp-sel');
10466             this.mpSelYear = pn.dom.xyear;
10467         }
10468         else if(el.is('a.x-date-mp-prev')){
10469             this.updateMPYear(this.mpyear-10);
10470         }
10471         else if(el.is('a.x-date-mp-next')){
10472             this.updateMPYear(this.mpyear+10);
10473         }
10474     },
10475
10476     onMonthDblClick : function(e, t){
10477         e.stopEvent();
10478         var el = new Roo.Element(t), pn;
10479         if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10481             this.hideMonthPicker();
10482         }
10483         else if(pn = el.up('td.x-date-mp-year', 2)){
10484             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10485             this.hideMonthPicker();
10486         }
10487     },
10488
10489     hideMonthPicker : function(disableAnim){
10490         if(this.monthPicker){
10491             if(disableAnim === true){
10492                 this.monthPicker.hide();
10493             }else{
10494                 this.monthPicker.slideOut('t', {duration:.2});
10495             }
10496         }
10497     },
10498
10499     // private
10500     showPrevMonth : function(e){
10501         this.update(this.activeDate.add("mo", -1));
10502     },
10503
10504     // private
10505     showNextMonth : function(e){
10506         this.update(this.activeDate.add("mo", 1));
10507     },
10508
10509     // private
10510     showPrevYear : function(){
10511         this.update(this.activeDate.add("y", -1));
10512     },
10513
10514     // private
10515     showNextYear : function(){
10516         this.update(this.activeDate.add("y", 1));
10517     },
10518
10519     // private
10520     handleMouseWheel : function(e){
10521         var delta = e.getWheelDelta();
10522         if(delta > 0){
10523             this.showPrevMonth();
10524             e.stopEvent();
10525         } else if(delta < 0){
10526             this.showNextMonth();
10527             e.stopEvent();
10528         }
10529     },
10530
10531     // private
10532     handleDateClick : function(e, t){
10533         e.stopEvent();
10534         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10535             this.setValue(new Date(t.dateValue));
10536             this.fireEvent("select", this, this.value);
10537         }
10538     },
10539
10540     // private
10541     selectToday : function(){
10542         this.setValue(new Date().clearTime());
10543         this.fireEvent("select", this, this.value);
10544     },
10545
10546     // private
10547     update : function(date){
10548         var vd = this.activeDate;
10549         this.activeDate = date;
10550         if(vd && this.el){
10551             var t = date.getTime();
10552             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10553                 this.cells.removeClass("x-date-selected");
10554                 this.cells.each(function(c){
10555                    if(c.dom.firstChild.dateValue == t){
10556                        c.addClass("x-date-selected");
10557                        setTimeout(function(){
10558                             try{c.dom.firstChild.focus();}catch(e){}
10559                        }, 50);
10560                        return false;
10561                    }
10562                 });
10563                 return;
10564             }
10565         }
10566         var days = date.getDaysInMonth();
10567         var firstOfMonth = date.getFirstDateOfMonth();
10568         var startingPos = firstOfMonth.getDay()-this.startDay;
10569
10570         if(startingPos <= this.startDay){
10571             startingPos += 7;
10572         }
10573
10574         var pm = date.add("mo", -1);
10575         var prevStart = pm.getDaysInMonth()-startingPos;
10576
10577         var cells = this.cells.elements;
10578         var textEls = this.textNodes;
10579         days += startingPos;
10580
10581         // convert everything to numbers so it's fast
10582         var day = 86400000;
10583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10584         var today = new Date().clearTime().getTime();
10585         var sel = date.clearTime().getTime();
10586         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10587         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10588         var ddMatch = this.disabledDatesRE;
10589         var ddText = this.disabledDatesText;
10590         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10591         var ddaysText = this.disabledDaysText;
10592         var format = this.format;
10593
10594         var setCellClass = function(cal, cell){
10595             cell.title = "";
10596             var t = d.getTime();
10597             cell.firstChild.dateValue = t;
10598             if(t == today){
10599                 cell.className += " x-date-today";
10600                 cell.title = cal.todayText;
10601             }
10602             if(t == sel){
10603                 cell.className += " x-date-selected";
10604                 setTimeout(function(){
10605                     try{cell.firstChild.focus();}catch(e){}
10606                 }, 50);
10607             }
10608             // disabling
10609             if(t < min) {
10610                 cell.className = " x-date-disabled";
10611                 cell.title = cal.minText;
10612                 return;
10613             }
10614             if(t > max) {
10615                 cell.className = " x-date-disabled";
10616                 cell.title = cal.maxText;
10617                 return;
10618             }
10619             if(ddays){
10620                 if(ddays.indexOf(d.getDay()) != -1){
10621                     cell.title = ddaysText;
10622                     cell.className = " x-date-disabled";
10623                 }
10624             }
10625             if(ddMatch && format){
10626                 var fvalue = d.dateFormat(format);
10627                 if(ddMatch.test(fvalue)){
10628                     cell.title = ddText.replace("%0", fvalue);
10629                     cell.className = " x-date-disabled";
10630                 }
10631             }
10632         };
10633
10634         var i = 0;
10635         for(; i < startingPos; i++) {
10636             textEls[i].innerHTML = (++prevStart);
10637             d.setDate(d.getDate()+1);
10638             cells[i].className = "x-date-prevday";
10639             setCellClass(this, cells[i]);
10640         }
10641         for(; i < days; i++){
10642             intDay = i - startingPos + 1;
10643             textEls[i].innerHTML = (intDay);
10644             d.setDate(d.getDate()+1);
10645             cells[i].className = "x-date-active";
10646             setCellClass(this, cells[i]);
10647         }
10648         var extraDays = 0;
10649         for(; i < 42; i++) {
10650              textEls[i].innerHTML = (++extraDays);
10651              d.setDate(d.getDate()+1);
10652              cells[i].className = "x-date-nextday";
10653              setCellClass(this, cells[i]);
10654         }
10655
10656         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10657
10658         if(!this.internalRender){
10659             var main = this.el.dom.firstChild;
10660             var w = main.offsetWidth;
10661             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10662             Roo.fly(main).setWidth(w);
10663             this.internalRender = true;
10664             // opera does not respect the auto grow header center column
10665             // then, after it gets a width opera refuses to recalculate
10666             // without a second pass
10667             if(Roo.isOpera && !this.secondPass){
10668                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10669                 this.secondPass = true;
10670                 this.update.defer(10, this, [date]);
10671             }
10672         }
10673     }
10674 });/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684 /**
10685  * @class Roo.TabPanel
10686  * @extends Roo.util.Observable
10687  * A lightweight tab container.
10688  * <br><br>
10689  * Usage:
10690  * <pre><code>
10691 // basic tabs 1, built from existing content
10692 var tabs = new Roo.TabPanel("tabs1");
10693 tabs.addTab("script", "View Script");
10694 tabs.addTab("markup", "View Markup");
10695 tabs.activate("script");
10696
10697 // more advanced tabs, built from javascript
10698 var jtabs = new Roo.TabPanel("jtabs");
10699 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10700
10701 // set up the UpdateManager
10702 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10703 var updater = tab2.getUpdateManager();
10704 updater.setDefaultUrl("ajax1.htm");
10705 tab2.on('activate', updater.refresh, updater, true);
10706
10707 // Use setUrl for Ajax loading
10708 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10709 tab3.setUrl("ajax2.htm", null, true);
10710
10711 // Disabled tab
10712 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10713 tab4.disable();
10714
10715 jtabs.activate("jtabs-1");
10716  * </code></pre>
10717  * @constructor
10718  * Create a new TabPanel.
10719  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10720  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10721  */
10722 Roo.TabPanel = function(container, config){
10723     /**
10724     * The container element for this TabPanel.
10725     * @type Roo.Element
10726     */
10727     this.el = Roo.get(container, true);
10728     if(config){
10729         if(typeof config == "boolean"){
10730             this.tabPosition = config ? "bottom" : "top";
10731         }else{
10732             Roo.apply(this, config);
10733         }
10734     }
10735     if(this.tabPosition == "bottom"){
10736         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10737         this.el.addClass("x-tabs-bottom");
10738     }
10739     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10740     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10741     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10742     if(Roo.isIE){
10743         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10744     }
10745     if(this.tabPosition != "bottom"){
10746     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10747      * @type Roo.Element
10748      */
10749       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10750       this.el.addClass("x-tabs-top");
10751     }
10752     this.items = [];
10753
10754     this.bodyEl.setStyle("position", "relative");
10755
10756     this.active = null;
10757     this.activateDelegate = this.activate.createDelegate(this);
10758
10759     this.addEvents({
10760         /**
10761          * @event tabchange
10762          * Fires when the active tab changes
10763          * @param {Roo.TabPanel} this
10764          * @param {Roo.TabPanelItem} activePanel The new active tab
10765          */
10766         "tabchange": true,
10767         /**
10768          * @event beforetabchange
10769          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10770          * @param {Roo.TabPanel} this
10771          * @param {Object} e Set cancel to true on this object to cancel the tab change
10772          * @param {Roo.TabPanelItem} tab The tab being changed to
10773          */
10774         "beforetabchange" : true
10775     });
10776
10777     Roo.EventManager.onWindowResize(this.onResize, this);
10778     this.cpad = this.el.getPadding("lr");
10779     this.hiddenCount = 0;
10780
10781     Roo.TabPanel.superclass.constructor.call(this);
10782 };
10783
10784 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10785         /*
10786          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10787          */
10788     tabPosition : "top",
10789         /*
10790          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10791          */
10792     currentTabWidth : 0,
10793         /*
10794          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10795          */
10796     minTabWidth : 40,
10797         /*
10798          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10799          */
10800     maxTabWidth : 250,
10801         /*
10802          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10803          */
10804     preferredTabWidth : 175,
10805         /*
10806          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10807          */
10808     resizeTabs : false,
10809         /*
10810          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10811          */
10812     monitorResize : true,
10813
10814     /**
10815      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10816      * @param {String} id The id of the div to use <b>or create</b>
10817      * @param {String} text The text for the tab
10818      * @param {String} content (optional) Content to put in the TabPanelItem body
10819      * @param {Boolean} closable (optional) True to create a close icon on the tab
10820      * @return {Roo.TabPanelItem} The created TabPanelItem
10821      */
10822     addTab : function(id, text, content, closable){
10823         var item = new Roo.TabPanelItem(this, id, text, closable);
10824         this.addTabItem(item);
10825         if(content){
10826             item.setContent(content);
10827         }
10828         return item;
10829     },
10830
10831     /**
10832      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10833      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10834      * @return {Roo.TabPanelItem}
10835      */
10836     getTab : function(id){
10837         return this.items[id];
10838     },
10839
10840     /**
10841      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10842      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10843      */
10844     hideTab : function(id){
10845         var t = this.items[id];
10846         if(!t.isHidden()){
10847            t.setHidden(true);
10848            this.hiddenCount++;
10849            this.autoSizeTabs();
10850         }
10851     },
10852
10853     /**
10854      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10855      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10856      */
10857     unhideTab : function(id){
10858         var t = this.items[id];
10859         if(t.isHidden()){
10860            t.setHidden(false);
10861            this.hiddenCount--;
10862            this.autoSizeTabs();
10863         }
10864     },
10865
10866     /**
10867      * Adds an existing {@link Roo.TabPanelItem}.
10868      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10869      */
10870     addTabItem : function(item){
10871         this.items[item.id] = item;
10872         this.items.push(item);
10873         if(this.resizeTabs){
10874            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10875            this.autoSizeTabs();
10876         }else{
10877             item.autoSize();
10878         }
10879     },
10880
10881     /**
10882      * Removes a {@link Roo.TabPanelItem}.
10883      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10884      */
10885     removeTab : function(id){
10886         var items = this.items;
10887         var tab = items[id];
10888         if(!tab) { return; }
10889         var index = items.indexOf(tab);
10890         if(this.active == tab && items.length > 1){
10891             var newTab = this.getNextAvailable(index);
10892             if(newTab) {
10893                 newTab.activate();
10894             }
10895         }
10896         this.stripEl.dom.removeChild(tab.pnode.dom);
10897         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10898             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10899         }
10900         items.splice(index, 1);
10901         delete this.items[tab.id];
10902         tab.fireEvent("close", tab);
10903         tab.purgeListeners();
10904         this.autoSizeTabs();
10905     },
10906
10907     getNextAvailable : function(start){
10908         var items = this.items;
10909         var index = start;
10910         // look for a next tab that will slide over to
10911         // replace the one being removed
10912         while(index < items.length){
10913             var item = items[++index];
10914             if(item && !item.isHidden()){
10915                 return item;
10916             }
10917         }
10918         // if one isn't found select the previous tab (on the left)
10919         index = start;
10920         while(index >= 0){
10921             var item = items[--index];
10922             if(item && !item.isHidden()){
10923                 return item;
10924             }
10925         }
10926         return null;
10927     },
10928
10929     /**
10930      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10931      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10932      */
10933     disableTab : function(id){
10934         var tab = this.items[id];
10935         if(tab && this.active != tab){
10936             tab.disable();
10937         }
10938     },
10939
10940     /**
10941      * Enables a {@link Roo.TabPanelItem} that is disabled.
10942      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10943      */
10944     enableTab : function(id){
10945         var tab = this.items[id];
10946         tab.enable();
10947     },
10948
10949     /**
10950      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10951      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10952      * @return {Roo.TabPanelItem} The TabPanelItem.
10953      */
10954     activate : function(id){
10955         var tab = this.items[id];
10956         if(!tab){
10957             return null;
10958         }
10959         if(tab == this.active || tab.disabled){
10960             return tab;
10961         }
10962         var e = {};
10963         this.fireEvent("beforetabchange", this, e, tab);
10964         if(e.cancel !== true && !tab.disabled){
10965             if(this.active){
10966                 this.active.hide();
10967             }
10968             this.active = this.items[id];
10969             this.active.show();
10970             this.fireEvent("tabchange", this, this.active);
10971         }
10972         return tab;
10973     },
10974
10975     /**
10976      * Gets the active {@link Roo.TabPanelItem}.
10977      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10978      */
10979     getActiveTab : function(){
10980         return this.active;
10981     },
10982
10983     /**
10984      * Updates the tab body element to fit the height of the container element
10985      * for overflow scrolling
10986      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10987      */
10988     syncHeight : function(targetHeight){
10989         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10990         var bm = this.bodyEl.getMargins();
10991         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10992         this.bodyEl.setHeight(newHeight);
10993         return newHeight;
10994     },
10995
10996     onResize : function(){
10997         if(this.monitorResize){
10998             this.autoSizeTabs();
10999         }
11000     },
11001
11002     /**
11003      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11004      */
11005     beginUpdate : function(){
11006         this.updating = true;
11007     },
11008
11009     /**
11010      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11011      */
11012     endUpdate : function(){
11013         this.updating = false;
11014         this.autoSizeTabs();
11015     },
11016
11017     /**
11018      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11019      */
11020     autoSizeTabs : function(){
11021         var count = this.items.length;
11022         var vcount = count - this.hiddenCount;
11023         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11024         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11025         var availWidth = Math.floor(w / vcount);
11026         var b = this.stripBody;
11027         if(b.getWidth() > w){
11028             var tabs = this.items;
11029             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11030             if(availWidth < this.minTabWidth){
11031                 /*if(!this.sleft){    // incomplete scrolling code
11032                     this.createScrollButtons();
11033                 }
11034                 this.showScroll();
11035                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11036             }
11037         }else{
11038             if(this.currentTabWidth < this.preferredTabWidth){
11039                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11040             }
11041         }
11042     },
11043
11044     /**
11045      * Returns the number of tabs in this TabPanel.
11046      * @return {Number}
11047      */
11048      getCount : function(){
11049          return this.items.length;
11050      },
11051
11052     /**
11053      * Resizes all the tabs to the passed width
11054      * @param {Number} The new width
11055      */
11056     setTabWidth : function(width){
11057         this.currentTabWidth = width;
11058         for(var i = 0, len = this.items.length; i < len; i++) {
11059                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11060         }
11061     },
11062
11063     /**
11064      * Destroys this TabPanel
11065      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11066      */
11067     destroy : function(removeEl){
11068         Roo.EventManager.removeResizeListener(this.onResize, this);
11069         for(var i = 0, len = this.items.length; i < len; i++){
11070             this.items[i].purgeListeners();
11071         }
11072         if(removeEl === true){
11073             this.el.update("");
11074             this.el.remove();
11075         }
11076     }
11077 });
11078
11079 /**
11080  * @class Roo.TabPanelItem
11081  * @extends Roo.util.Observable
11082  * Represents an individual item (tab plus body) in a TabPanel.
11083  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11084  * @param {String} id The id of this TabPanelItem
11085  * @param {String} text The text for the tab of this TabPanelItem
11086  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11087  */
11088 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11089     /**
11090      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11091      * @type Roo.TabPanel
11092      */
11093     this.tabPanel = tabPanel;
11094     /**
11095      * The id for this TabPanelItem
11096      * @type String
11097      */
11098     this.id = id;
11099     /** @private */
11100     this.disabled = false;
11101     /** @private */
11102     this.text = text;
11103     /** @private */
11104     this.loaded = false;
11105     this.closable = closable;
11106
11107     /**
11108      * The body element for this TabPanelItem.
11109      * @type Roo.Element
11110      */
11111     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11112     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11113     this.bodyEl.setStyle("display", "block");
11114     this.bodyEl.setStyle("zoom", "1");
11115     this.hideAction();
11116
11117     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11118     /** @private */
11119     this.el = Roo.get(els.el, true);
11120     this.inner = Roo.get(els.inner, true);
11121     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11122     this.pnode = Roo.get(els.el.parentNode, true);
11123     this.el.on("mousedown", this.onTabMouseDown, this);
11124     this.el.on("click", this.onTabClick, this);
11125     /** @private */
11126     if(closable){
11127         var c = Roo.get(els.close, true);
11128         c.dom.title = this.closeText;
11129         c.addClassOnOver("close-over");
11130         c.on("click", this.closeClick, this);
11131      }
11132
11133     this.addEvents({
11134          /**
11135          * @event activate
11136          * Fires when this tab becomes the active tab.
11137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11138          * @param {Roo.TabPanelItem} this
11139          */
11140         "activate": true,
11141         /**
11142          * @event beforeclose
11143          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11144          * @param {Roo.TabPanelItem} this
11145          * @param {Object} e Set cancel to true on this object to cancel the close.
11146          */
11147         "beforeclose": true,
11148         /**
11149          * @event close
11150          * Fires when this tab is closed.
11151          * @param {Roo.TabPanelItem} this
11152          */
11153          "close": true,
11154         /**
11155          * @event deactivate
11156          * Fires when this tab is no longer the active tab.
11157          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11158          * @param {Roo.TabPanelItem} this
11159          */
11160          "deactivate" : true
11161     });
11162     this.hidden = false;
11163
11164     Roo.TabPanelItem.superclass.constructor.call(this);
11165 };
11166
11167 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11168     purgeListeners : function(){
11169        Roo.util.Observable.prototype.purgeListeners.call(this);
11170        this.el.removeAllListeners();
11171     },
11172     /**
11173      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11174      */
11175     show : function(){
11176         this.pnode.addClass("on");
11177         this.showAction();
11178         if(Roo.isOpera){
11179             this.tabPanel.stripWrap.repaint();
11180         }
11181         this.fireEvent("activate", this.tabPanel, this);
11182     },
11183
11184     /**
11185      * Returns true if this tab is the active tab.
11186      * @return {Boolean}
11187      */
11188     isActive : function(){
11189         return this.tabPanel.getActiveTab() == this;
11190     },
11191
11192     /**
11193      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11194      */
11195     hide : function(){
11196         this.pnode.removeClass("on");
11197         this.hideAction();
11198         this.fireEvent("deactivate", this.tabPanel, this);
11199     },
11200
11201     hideAction : function(){
11202         this.bodyEl.hide();
11203         this.bodyEl.setStyle("position", "absolute");
11204         this.bodyEl.setLeft("-20000px");
11205         this.bodyEl.setTop("-20000px");
11206     },
11207
11208     showAction : function(){
11209         this.bodyEl.setStyle("position", "relative");
11210         this.bodyEl.setTop("");
11211         this.bodyEl.setLeft("");
11212         this.bodyEl.show();
11213     },
11214
11215     /**
11216      * Set the tooltip for the tab.
11217      * @param {String} tooltip The tab's tooltip
11218      */
11219     setTooltip : function(text){
11220         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11221             this.textEl.dom.qtip = text;
11222             this.textEl.dom.removeAttribute('title');
11223         }else{
11224             this.textEl.dom.title = text;
11225         }
11226     },
11227
11228     onTabClick : function(e){
11229         e.preventDefault();
11230         this.tabPanel.activate(this.id);
11231     },
11232
11233     onTabMouseDown : function(e){
11234         e.preventDefault();
11235         this.tabPanel.activate(this.id);
11236     },
11237
11238     getWidth : function(){
11239         return this.inner.getWidth();
11240     },
11241
11242     setWidth : function(width){
11243         var iwidth = width - this.pnode.getPadding("lr");
11244         this.inner.setWidth(iwidth);
11245         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11246         this.pnode.setWidth(width);
11247     },
11248
11249     /**
11250      * Show or hide the tab
11251      * @param {Boolean} hidden True to hide or false to show.
11252      */
11253     setHidden : function(hidden){
11254         this.hidden = hidden;
11255         this.pnode.setStyle("display", hidden ? "none" : "");
11256     },
11257
11258     /**
11259      * Returns true if this tab is "hidden"
11260      * @return {Boolean}
11261      */
11262     isHidden : function(){
11263         return this.hidden;
11264     },
11265
11266     /**
11267      * Returns the text for this tab
11268      * @return {String}
11269      */
11270     getText : function(){
11271         return this.text;
11272     },
11273
11274     autoSize : function(){
11275         //this.el.beginMeasure();
11276         this.textEl.setWidth(1);
11277         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11278         //this.el.endMeasure();
11279     },
11280
11281     /**
11282      * Sets the text for the tab (Note: this also sets the tooltip text)
11283      * @param {String} text The tab's text and tooltip
11284      */
11285     setText : function(text){
11286         this.text = text;
11287         this.textEl.update(text);
11288         this.setTooltip(text);
11289         if(!this.tabPanel.resizeTabs){
11290             this.autoSize();
11291         }
11292     },
11293     /**
11294      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11295      */
11296     activate : function(){
11297         this.tabPanel.activate(this.id);
11298     },
11299
11300     /**
11301      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11302      */
11303     disable : function(){
11304         if(this.tabPanel.active != this){
11305             this.disabled = true;
11306             this.pnode.addClass("disabled");
11307         }
11308     },
11309
11310     /**
11311      * Enables this TabPanelItem if it was previously disabled.
11312      */
11313     enable : function(){
11314         this.disabled = false;
11315         this.pnode.removeClass("disabled");
11316     },
11317
11318     /**
11319      * Sets the content for this TabPanelItem.
11320      * @param {String} content The content
11321      * @param {Boolean} loadScripts true to look for and load scripts
11322      */
11323     setContent : function(content, loadScripts){
11324         this.bodyEl.update(content, loadScripts);
11325     },
11326
11327     /**
11328      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11329      * @return {Roo.UpdateManager} The UpdateManager
11330      */
11331     getUpdateManager : function(){
11332         return this.bodyEl.getUpdateManager();
11333     },
11334
11335     /**
11336      * Set a URL to be used to load the content for this TabPanelItem.
11337      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11338      * @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)
11339      * @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)
11340      * @return {Roo.UpdateManager} The UpdateManager
11341      */
11342     setUrl : function(url, params, loadOnce){
11343         if(this.refreshDelegate){
11344             this.un('activate', this.refreshDelegate);
11345         }
11346         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11347         this.on("activate", this.refreshDelegate);
11348         return this.bodyEl.getUpdateManager();
11349     },
11350
11351     /** @private */
11352     _handleRefresh : function(url, params, loadOnce){
11353         if(!loadOnce || !this.loaded){
11354             var updater = this.bodyEl.getUpdateManager();
11355             updater.update(url, params, this._setLoaded.createDelegate(this));
11356         }
11357     },
11358
11359     /**
11360      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11361      *   Will fail silently if the setUrl method has not been called.
11362      *   This does not activate the panel, just updates its content.
11363      */
11364     refresh : function(){
11365         if(this.refreshDelegate){
11366            this.loaded = false;
11367            this.refreshDelegate();
11368         }
11369     },
11370
11371     /** @private */
11372     _setLoaded : function(){
11373         this.loaded = true;
11374     },
11375
11376     /** @private */
11377     closeClick : function(e){
11378         var o = {};
11379         e.stopEvent();
11380         this.fireEvent("beforeclose", this, o);
11381         if(o.cancel !== true){
11382             this.tabPanel.removeTab(this.id);
11383         }
11384     },
11385     /**
11386      * The text displayed in the tooltip for the close icon.
11387      * @type String
11388      */
11389     closeText : "Close this tab"
11390 });
11391
11392 /** @private */
11393 Roo.TabPanel.prototype.createStrip = function(container){
11394     var strip = document.createElement("div");
11395     strip.className = "x-tabs-wrap";
11396     container.appendChild(strip);
11397     return strip;
11398 };
11399 /** @private */
11400 Roo.TabPanel.prototype.createStripList = function(strip){
11401     // div wrapper for retard IE
11402     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>';
11403     return strip.firstChild.firstChild.firstChild.firstChild;
11404 };
11405 /** @private */
11406 Roo.TabPanel.prototype.createBody = function(container){
11407     var body = document.createElement("div");
11408     Roo.id(body, "tab-body");
11409     Roo.fly(body).addClass("x-tabs-body");
11410     container.appendChild(body);
11411     return body;
11412 };
11413 /** @private */
11414 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11415     var body = Roo.getDom(id);
11416     if(!body){
11417         body = document.createElement("div");
11418         body.id = id;
11419     }
11420     Roo.fly(body).addClass("x-tabs-item-body");
11421     bodyEl.insertBefore(body, bodyEl.firstChild);
11422     return body;
11423 };
11424 /** @private */
11425 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11426     var td = document.createElement("td");
11427     stripEl.appendChild(td);
11428     if(closable){
11429         td.className = "x-tabs-closable";
11430         if(!this.closeTpl){
11431             this.closeTpl = new Roo.Template(
11432                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11433                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11434                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11435             );
11436         }
11437         var el = this.closeTpl.overwrite(td, {"text": text});
11438         var close = el.getElementsByTagName("div")[0];
11439         var inner = el.getElementsByTagName("em")[0];
11440         return {"el": el, "close": close, "inner": inner};
11441     } else {
11442         if(!this.tabTpl){
11443             this.tabTpl = new Roo.Template(
11444                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11445                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11446             );
11447         }
11448         var el = this.tabTpl.overwrite(td, {"text": text});
11449         var inner = el.getElementsByTagName("em")[0];
11450         return {"el": el, "inner": inner};
11451     }
11452 };