roojs-core-debug.js
[roojs1] / roojs-core-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
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358
359         /**
360          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
361          * @param {String} string
362          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
363          * @return {Object} A literal with members
364          */
365         urlDecode : function(string, overwrite){
366             if(!string || !string.length){
367                 return {};
368             }
369             var obj = {};
370             var pairs = string.split('&');
371             var pair, name, value;
372             for(var i = 0, len = pairs.length; i < len; i++){
373                 pair = pairs[i].split('=');
374                 name = decodeURIComponent(pair[0]);
375                 value = decodeURIComponent(pair[1]);
376                 if(overwrite !== true){
377                     if(typeof obj[name] == "undefined"){
378                         obj[name] = value;
379                     }else if(typeof obj[name] == "string"){
380                         obj[name] = [obj[name]];
381                         obj[name].push(value);
382                     }else{
383                         obj[name].push(value);
384                     }
385                 }else{
386                     obj[name] = value;
387                 }
388             }
389             return obj;
390         },
391
392         /**
393          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
394          * passed array is not really an array, your function is called once with it.
395          * The supplied function is called with (Object item, Number index, Array allItems).
396          * @param {Array/NodeList/Mixed} array
397          * @param {Function} fn
398          * @param {Object} scope
399          */
400         each : function(array, fn, scope){
401             if(typeof array.length == "undefined" || typeof array == "string"){
402                 array = [array];
403             }
404             for(var i = 0, len = array.length; i < len; i++){
405                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
406             }
407         },
408
409         // deprecated
410         combine : function(){
411             var as = arguments, l = as.length, r = [];
412             for(var i = 0; i < l; i++){
413                 var a = as[i];
414                 if(a instanceof Array){
415                     r = r.concat(a);
416                 }else if(a.length !== undefined && !a.substr){
417                     r = r.concat(Array.prototype.slice.call(a, 0));
418                 }else{
419                     r.push(a);
420                 }
421             }
422             return r;
423         },
424
425         /**
426          * Escapes the passed string for use in a regular expression
427          * @param {String} str
428          * @return {String}
429          */
430         escapeRe : function(s) {
431             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
432         },
433
434         // internal
435         callback : function(cb, scope, args, delay){
436             if(typeof cb == "function"){
437                 if(delay){
438                     cb.defer(delay, scope, args || []);
439                 }else{
440                     cb.apply(scope, args || []);
441                 }
442             }
443         },
444
445         /**
446          * Return the dom node for the passed string (id), dom node, or Roo.Element
447          * @param {String/HTMLElement/Roo.Element} el
448          * @return HTMLElement
449          */
450         getDom : function(el){
451             if(!el){
452                 return null;
453             }
454             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
455         },
456
457         /**
458         * Shorthand for {@link Roo.ComponentMgr#get}
459         * @param {String} id
460         * @return Roo.Component
461         */
462         getCmp : function(id){
463             return Roo.ComponentMgr.get(id);
464         },
465          
466         num : function(v, defaultValue){
467             if(typeof v != 'number'){
468                 return defaultValue;
469             }
470             return v;
471         },
472
473         destroy : function(){
474             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
475                 var as = a[i];
476                 if(as){
477                     if(as.dom){
478                         as.removeAllListeners();
479                         as.remove();
480                         continue;
481                     }
482                     if(typeof as.purgeListeners == 'function'){
483                         as.purgeListeners();
484                     }
485                     if(typeof as.destroy == 'function'){
486                         as.destroy();
487                     }
488                 }
489             }
490         },
491
492         // inpired by a similar function in mootools library
493         /**
494          * Returns the type of object that is passed in. If the object passed in is null or undefined it
495          * return false otherwise it returns one of the following values:<ul>
496          * <li><b>string</b>: If the object passed is a string</li>
497          * <li><b>number</b>: If the object passed is a number</li>
498          * <li><b>boolean</b>: If the object passed is a boolean value</li>
499          * <li><b>function</b>: If the object passed is a function reference</li>
500          * <li><b>object</b>: If the object passed is an object</li>
501          * <li><b>array</b>: If the object passed is an array</li>
502          * <li><b>regexp</b>: If the object passed is a regular expression</li>
503          * <li><b>element</b>: If the object passed is a DOM Element</li>
504          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
505          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
506          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
507          * @param {Mixed} object
508          * @return {String}
509          */
510         type : function(o){
511             if(o === undefined || o === null){
512                 return false;
513             }
514             if(o.htmlElement){
515                 return 'element';
516             }
517             var t = typeof o;
518             if(t == 'object' && o.nodeName) {
519                 switch(o.nodeType) {
520                     case 1: return 'element';
521                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
522                 }
523             }
524             if(t == 'object' || t == 'function') {
525                 switch(o.constructor) {
526                     case Array: return 'array';
527                     case RegExp: return 'regexp';
528                 }
529                 if(typeof o.length == 'number' && typeof o.item == 'function') {
530                     return 'nodelist';
531                 }
532             }
533             return t;
534         },
535
536         /**
537          * Returns true if the passed value is null, undefined or an empty string (optional).
538          * @param {Mixed} value The value to test
539          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
540          * @return {Boolean}
541          */
542         isEmpty : function(v, allowBlank){
543             return v === null || v === undefined || (!allowBlank ? v === '' : false);
544         },
545         
546         /** @type Boolean */
547         isOpera : isOpera,
548         /** @type Boolean */
549         isSafari : isSafari,
550         /** @type Boolean */
551         isIE : isIE,
552         /** @type Boolean */
553         isIE7 : isIE7,
554         /** @type Boolean */
555         isGecko : isGecko,
556         /** @type Boolean */
557         isBorderBox : isBorderBox,
558         /** @type Boolean */
559         isWindows : isWindows,
560         /** @type Boolean */
561         isLinux : isLinux,
562         /** @type Boolean */
563         isMac : isMac,
564
565         /**
566          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
567          * you may want to set this to true.
568          * @type Boolean
569          */
570         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
571         
572         
573                 
574         /**
575          * Selects a single element as a Roo Element
576          * This is about as close as you can get to jQuery's $('do crazy stuff')
577          * @param {String} selector The selector/xpath query
578          * @param {Node} root (optional) The start of the query (defaults to document).
579          * @return {Roo.Element}
580          */
581         selectNode : element(selector, root) 
582         {
583             var node = Roo.DomQuery.selectNode(selector,root);
584             return node ? Roo.get(node) : false;
585         }
586         
587     });
588
589
590 })();
591
592 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
593                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
594 /*
595  * Based on:
596  * Ext JS Library 1.1.1
597  * Copyright(c) 2006-2007, Ext JS, LLC.
598  *
599  * Originally Released Under LGPL - original licence link has changed is not relivant.
600  *
601  * Fork - LGPL
602  * <script type="text/javascript">
603  */
604
605 (function() {    
606     // wrappedn so fnCleanup is not in global scope...
607     if(Roo.isIE) {
608         function fnCleanUp() {
609             var p = Function.prototype;
610             delete p.createSequence;
611             delete p.defer;
612             delete p.createDelegate;
613             delete p.createCallback;
614             delete p.createInterceptor;
615
616             window.detachEvent("onunload", fnCleanUp);
617         }
618         window.attachEvent("onunload", fnCleanUp);
619     }
620 })();
621
622
623 /**
624  * @class Function
625  * These functions are available on every Function object (any JavaScript function).
626  */
627 Roo.apply(Function.prototype, {
628      /**
629      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
630      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
631      * Will create a function that is bound to those 2 args.
632      * @return {Function} The new function
633     */
634     createCallback : function(/*args...*/){
635         // make args available, in function below
636         var args = arguments;
637         var method = this;
638         return function() {
639             return method.apply(window, args);
640         };
641     },
642
643     /**
644      * Creates a delegate (callback) that sets the scope to obj.
645      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
646      * Will create a function that is automatically scoped to this.
647      * @param {Object} obj (optional) The object for which the scope is set
648      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
649      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
650      *                                             if a number the args are inserted at the specified position
651      * @return {Function} The new function
652      */
653     createDelegate : function(obj, args, appendArgs){
654         var method = this;
655         return function() {
656             var callArgs = args || arguments;
657             if(appendArgs === true){
658                 callArgs = Array.prototype.slice.call(arguments, 0);
659                 callArgs = callArgs.concat(args);
660             }else if(typeof appendArgs == "number"){
661                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
662                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
663                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
664             }
665             return method.apply(obj || window, callArgs);
666         };
667     },
668
669     /**
670      * Calls this function after the number of millseconds specified.
671      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
672      * @param {Object} obj (optional) The object for which the scope is set
673      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
674      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
675      *                                             if a number the args are inserted at the specified position
676      * @return {Number} The timeout id that can be used with clearTimeout
677      */
678     defer : function(millis, obj, args, appendArgs){
679         var fn = this.createDelegate(obj, args, appendArgs);
680         if(millis){
681             return setTimeout(fn, millis);
682         }
683         fn();
684         return 0;
685     },
686     /**
687      * Create a combined function call sequence of the original function + the passed function.
688      * The resulting function returns the results of the original function.
689      * The passed fcn is called with the parameters of the original function
690      * @param {Function} fcn The function to sequence
691      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
692      * @return {Function} The new function
693      */
694     createSequence : function(fcn, scope){
695         if(typeof fcn != "function"){
696             return this;
697         }
698         var method = this;
699         return function() {
700             var retval = method.apply(this || window, arguments);
701             fcn.apply(scope || this || window, arguments);
702             return retval;
703         };
704     },
705
706     /**
707      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
708      * The resulting function returns the results of the original function.
709      * The passed fcn is called with the parameters of the original function.
710      * @addon
711      * @param {Function} fcn The function to call before the original
712      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
713      * @return {Function} The new function
714      */
715     createInterceptor : function(fcn, scope){
716         if(typeof fcn != "function"){
717             return this;
718         }
719         var method = this;
720         return function() {
721             fcn.target = this;
722             fcn.method = method;
723             if(fcn.apply(scope || this || window, arguments) === false){
724                 return;
725             }
726             return method.apply(this || window, arguments);
727         };
728     }
729 });
730 /*
731  * Based on:
732  * Ext JS Library 1.1.1
733  * Copyright(c) 2006-2007, Ext JS, LLC.
734  *
735  * Originally Released Under LGPL - original licence link has changed is not relivant.
736  *
737  * Fork - LGPL
738  * <script type="text/javascript">
739  */
740
741 Roo.applyIf(String, {
742     
743     /** @scope String */
744     
745     /**
746      * Escapes the passed string for ' and \
747      * @param {String} string The string to escape
748      * @return {String} The escaped string
749      * @static
750      */
751     escape : function(string) {
752         return string.replace(/('|\\)/g, "\\$1");
753     },
754
755     /**
756      * Pads the left side of a string with a specified character.  This is especially useful
757      * for normalizing number and date strings.  Example usage:
758      * <pre><code>
759 var s = String.leftPad('123', 5, '0');
760 // s now contains the string: '00123'
761 </code></pre>
762      * @param {String} string The original string
763      * @param {Number} size The total length of the output string
764      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
765      * @return {String} The padded string
766      * @static
767      */
768     leftPad : function (val, size, ch) {
769         var result = new String(val);
770         if(ch === null || ch === undefined || ch === '') {
771             ch = " ";
772         }
773         while (result.length < size) {
774             result = ch + result;
775         }
776         return result;
777     },
778
779     /**
780      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
781      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
782      * <pre><code>
783 var cls = 'my-class', text = 'Some text';
784 var s = String.format('<div class="{0}">{1}</div>', cls, text);
785 // s now contains the string: '<div class="my-class">Some text</div>'
786 </code></pre>
787      * @param {String} string The tokenized string to be formatted
788      * @param {String} value1 The value to replace token {0}
789      * @param {String} value2 Etc...
790      * @return {String} The formatted string
791      * @static
792      */
793     format : function(format){
794         var args = Array.prototype.slice.call(arguments, 1);
795         return format.replace(/\{(\d+)\}/g, function(m, i){
796             return Roo.util.Format.htmlEncode(args[i]);
797         });
798     }
799 });
800
801 /**
802  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
803  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
804  * they are already different, the first value passed in is returned.  Note that this method returns the new value
805  * but does not change the current string.
806  * <pre><code>
807 // alternate sort directions
808 sort = sort.toggle('ASC', 'DESC');
809
810 // instead of conditional logic:
811 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
812 </code></pre>
813  * @param {String} value The value to compare to the current string
814  * @param {String} other The new value to use if the string already equals the first value passed in
815  * @return {String} The new value
816  */
817  
818 String.prototype.toggle = function(value, other){
819     return this == value ? other : value;
820 };/*
821  * Based on:
822  * Ext JS Library 1.1.1
823  * Copyright(c) 2006-2007, Ext JS, LLC.
824  *
825  * Originally Released Under LGPL - original licence link has changed is not relivant.
826  *
827  * Fork - LGPL
828  * <script type="text/javascript">
829  */
830
831  /**
832  * @class Number
833  */
834 Roo.applyIf(Number.prototype, {
835     /**
836      * Checks whether or not the current number is within a desired range.  If the number is already within the
837      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
838      * exceeded.  Note that this method returns the constrained value but does not change the current number.
839      * @param {Number} min The minimum number in the range
840      * @param {Number} max The maximum number in the range
841      * @return {Number} The constrained value if outside the range, otherwise the current value
842      */
843     constrain : function(min, max){
844         return Math.min(Math.max(this, min), max);
845     }
846 });/*
847  * Based on:
848  * Ext JS Library 1.1.1
849  * Copyright(c) 2006-2007, Ext JS, LLC.
850  *
851  * Originally Released Under LGPL - original licence link has changed is not relivant.
852  *
853  * Fork - LGPL
854  * <script type="text/javascript">
855  */
856  /**
857  * @class Array
858  */
859 Roo.applyIf(Array.prototype, {
860     /**
861      * Checks whether or not the specified object exists in the array.
862      * @param {Object} o The object to check for
863      * @return {Number} The index of o in the array (or -1 if it is not found)
864      */
865     indexOf : function(o){
866        for (var i = 0, len = this.length; i < len; i++){
867               if(this[i] == o) return i;
868        }
869            return -1;
870     },
871
872     /**
873      * Removes the specified object from the array.  If the object is not found nothing happens.
874      * @param {Object} o The object to remove
875      */
876     remove : function(o){
877        var index = this.indexOf(o);
878        if(index != -1){
879            this.splice(index, 1);
880        }
881     },
882     /**
883      * Map (JS 1.6 compatibility)
884      * @param {Function} function  to call
885      */
886     map : function(fun )
887     {
888         var len = this.length >>> 0;
889         if (typeof fun != "function")
890             throw new TypeError();
891
892         var res = new Array(len);
893         var thisp = arguments[1];
894         for (var i = 0; i < len; i++)
895         {
896             if (i in this)
897                 res[i] = fun.call(thisp, this[i], i, this);
898         }
899
900         return res;
901     }
902     
903 });
904
905
906  /*
907  * Based on:
908  * Ext JS Library 1.1.1
909  * Copyright(c) 2006-2007, Ext JS, LLC.
910  *
911  * Originally Released Under LGPL - original licence link has changed is not relivant.
912  *
913  * Fork - LGPL
914  * <script type="text/javascript">
915  */
916
917 /**
918  * @class Date
919  *
920  * The date parsing and format syntax is a subset of
921  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
922  * supported will provide results equivalent to their PHP versions.
923  *
924  * Following is the list of all currently supported formats:
925  *<pre>
926 Sample date:
927 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
928
929 Format  Output      Description
930 ------  ----------  --------------------------------------------------------------
931   d      10         Day of the month, 2 digits with leading zeros
932   D      Wed        A textual representation of a day, three letters
933   j      10         Day of the month without leading zeros
934   l      Wednesday  A full textual representation of the day of the week
935   S      th         English ordinal day of month suffix, 2 chars (use with j)
936   w      3          Numeric representation of the day of the week
937   z      9          The julian date, or day of the year (0-365)
938   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
939   F      January    A full textual representation of the month
940   m      01         Numeric representation of a month, with leading zeros
941   M      Jan        Month name abbreviation, three letters
942   n      1          Numeric representation of a month, without leading zeros
943   t      31         Number of days in the given month
944   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
945   Y      2007       A full numeric representation of a year, 4 digits
946   y      07         A two digit representation of a year
947   a      pm         Lowercase Ante meridiem and Post meridiem
948   A      PM         Uppercase Ante meridiem and Post meridiem
949   g      3          12-hour format of an hour without leading zeros
950   G      15         24-hour format of an hour without leading zeros
951   h      03         12-hour format of an hour with leading zeros
952   H      15         24-hour format of an hour with leading zeros
953   i      05         Minutes with leading zeros
954   s      01         Seconds, with leading zeros
955   O      -0600      Difference to Greenwich time (GMT) in hours
956   T      CST        Timezone setting of the machine running the code
957   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
958 </pre>
959  *
960  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
961  * <pre><code>
962 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
963 document.write(dt.format('Y-m-d'));                         //2007-01-10
964 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
965 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
966  </code></pre>
967  *
968  * Here are some standard date/time patterns that you might find helpful.  They
969  * are not part of the source of Date.js, but to use them you can simply copy this
970  * block of code into any script that is included after Date.js and they will also become
971  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
972  * <pre><code>
973 Date.patterns = {
974     ISO8601Long:"Y-m-d H:i:s",
975     ISO8601Short:"Y-m-d",
976     ShortDate: "n/j/Y",
977     LongDate: "l, F d, Y",
978     FullDateTime: "l, F d, Y g:i:s A",
979     MonthDay: "F d",
980     ShortTime: "g:i A",
981     LongTime: "g:i:s A",
982     SortableDateTime: "Y-m-d\\TH:i:s",
983     UniversalSortableDateTime: "Y-m-d H:i:sO",
984     YearMonth: "F, Y"
985 };
986 </code></pre>
987  *
988  * Example usage:
989  * <pre><code>
990 var dt = new Date();
991 document.write(dt.format(Date.patterns.ShortDate));
992  </code></pre>
993  */
994
995 /*
996  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
997  * They generate precompiled functions from date formats instead of parsing and
998  * processing the pattern every time you format a date.  These functions are available
999  * on every Date object (any javascript function).
1000  *
1001  * The original article and download are here:
1002  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1003  *
1004  */
1005  
1006  
1007  // was in core
1008 /**
1009  Returns the number of milliseconds between this date and date
1010  @param {Date} date (optional) Defaults to now
1011  @return {Number} The diff in milliseconds
1012  @member Date getElapsed
1013  */
1014 Date.prototype.getElapsed = function(date) {
1015         return Math.abs((date || new Date()).getTime()-this.getTime());
1016 };
1017 // was in date file..
1018
1019
1020 // private
1021 Date.parseFunctions = {count:0};
1022 // private
1023 Date.parseRegexes = [];
1024 // private
1025 Date.formatFunctions = {count:0};
1026
1027 // private
1028 Date.prototype.dateFormat = function(format) {
1029     if (Date.formatFunctions[format] == null) {
1030         Date.createNewFormat(format);
1031     }
1032     var func = Date.formatFunctions[format];
1033     return this[func]();
1034 };
1035
1036
1037 /**
1038  * Formats a date given the supplied format string
1039  * @param {String} format The format string
1040  * @return {String} The formatted date
1041  * @method
1042  */
1043 Date.prototype.format = Date.prototype.dateFormat;
1044
1045 // private
1046 Date.createNewFormat = function(format) {
1047     var funcName = "format" + Date.formatFunctions.count++;
1048     Date.formatFunctions[format] = funcName;
1049     var code = "Date.prototype." + funcName + " = function(){return ";
1050     var special = false;
1051     var ch = '';
1052     for (var i = 0; i < format.length; ++i) {
1053         ch = format.charAt(i);
1054         if (!special && ch == "\\") {
1055             special = true;
1056         }
1057         else if (special) {
1058             special = false;
1059             code += "'" + String.escape(ch) + "' + ";
1060         }
1061         else {
1062             code += Date.getFormatCode(ch);
1063         }
1064     }
1065     /** eval:var:zzzzzzzzzzzzz */
1066     eval(code.substring(0, code.length - 3) + ";}");
1067 };
1068
1069 // private
1070 Date.getFormatCode = function(character) {
1071     switch (character) {
1072     case "d":
1073         return "String.leftPad(this.getDate(), 2, '0') + ";
1074     case "D":
1075         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1076     case "j":
1077         return "this.getDate() + ";
1078     case "l":
1079         return "Date.dayNames[this.getDay()] + ";
1080     case "S":
1081         return "this.getSuffix() + ";
1082     case "w":
1083         return "this.getDay() + ";
1084     case "z":
1085         return "this.getDayOfYear() + ";
1086     case "W":
1087         return "this.getWeekOfYear() + ";
1088     case "F":
1089         return "Date.monthNames[this.getMonth()] + ";
1090     case "m":
1091         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1092     case "M":
1093         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1094     case "n":
1095         return "(this.getMonth() + 1) + ";
1096     case "t":
1097         return "this.getDaysInMonth() + ";
1098     case "L":
1099         return "(this.isLeapYear() ? 1 : 0) + ";
1100     case "Y":
1101         return "this.getFullYear() + ";
1102     case "y":
1103         return "('' + this.getFullYear()).substring(2, 4) + ";
1104     case "a":
1105         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1106     case "A":
1107         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1108     case "g":
1109         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1110     case "G":
1111         return "this.getHours() + ";
1112     case "h":
1113         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1114     case "H":
1115         return "String.leftPad(this.getHours(), 2, '0') + ";
1116     case "i":
1117         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1118     case "s":
1119         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1120     case "O":
1121         return "this.getGMTOffset() + ";
1122     case "T":
1123         return "this.getTimezone() + ";
1124     case "Z":
1125         return "(this.getTimezoneOffset() * -60) + ";
1126     default:
1127         return "'" + String.escape(character) + "' + ";
1128     }
1129 };
1130
1131 /**
1132  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1133  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1134  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1135  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1136  * string or the parse operation will fail.
1137  * Example Usage:
1138 <pre><code>
1139 //dt = Fri May 25 2007 (current date)
1140 var dt = new Date();
1141
1142 //dt = Thu May 25 2006 (today's month/day in 2006)
1143 dt = Date.parseDate("2006", "Y");
1144
1145 //dt = Sun Jan 15 2006 (all date parts specified)
1146 dt = Date.parseDate("2006-1-15", "Y-m-d");
1147
1148 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1149 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1150 </code></pre>
1151  * @param {String} input The unparsed date as a string
1152  * @param {String} format The format the date is in
1153  * @return {Date} The parsed date
1154  * @static
1155  */
1156 Date.parseDate = function(input, format) {
1157     if (Date.parseFunctions[format] == null) {
1158         Date.createParser(format);
1159     }
1160     var func = Date.parseFunctions[format];
1161     return Date[func](input);
1162 };
1163 /**
1164  * @private
1165  */
1166 Date.createParser = function(format) {
1167     var funcName = "parse" + Date.parseFunctions.count++;
1168     var regexNum = Date.parseRegexes.length;
1169     var currentGroup = 1;
1170     Date.parseFunctions[format] = funcName;
1171
1172     var code = "Date." + funcName + " = function(input){\n"
1173         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1174         + "var d = new Date();\n"
1175         + "y = d.getFullYear();\n"
1176         + "m = d.getMonth();\n"
1177         + "d = d.getDate();\n"
1178         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1179         + "if (results && results.length > 0) {";
1180     var regex = "";
1181
1182     var special = false;
1183     var ch = '';
1184     for (var i = 0; i < format.length; ++i) {
1185         ch = format.charAt(i);
1186         if (!special && ch == "\\") {
1187             special = true;
1188         }
1189         else if (special) {
1190             special = false;
1191             regex += String.escape(ch);
1192         }
1193         else {
1194             var obj = Date.formatCodeToRegex(ch, currentGroup);
1195             currentGroup += obj.g;
1196             regex += obj.s;
1197             if (obj.g && obj.c) {
1198                 code += obj.c;
1199             }
1200         }
1201     }
1202
1203     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1204         + "{v = new Date(y, m, d, h, i, s);}\n"
1205         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1206         + "{v = new Date(y, m, d, h, i);}\n"
1207         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1208         + "{v = new Date(y, m, d, h);}\n"
1209         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1210         + "{v = new Date(y, m, d);}\n"
1211         + "else if (y >= 0 && m >= 0)\n"
1212         + "{v = new Date(y, m);}\n"
1213         + "else if (y >= 0)\n"
1214         + "{v = new Date(y);}\n"
1215         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1216         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1217         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1218         + ";}";
1219
1220     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1221     /** eval:var:zzzzzzzzzzzzz */
1222     eval(code);
1223 };
1224
1225 // private
1226 Date.formatCodeToRegex = function(character, currentGroup) {
1227     switch (character) {
1228     case "D":
1229         return {g:0,
1230         c:null,
1231         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1232     case "j":
1233         return {g:1,
1234             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1235             s:"(\\d{1,2})"}; // day of month without leading zeroes
1236     case "d":
1237         return {g:1,
1238             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1239             s:"(\\d{2})"}; // day of month with leading zeroes
1240     case "l":
1241         return {g:0,
1242             c:null,
1243             s:"(?:" + Date.dayNames.join("|") + ")"};
1244     case "S":
1245         return {g:0,
1246             c:null,
1247             s:"(?:st|nd|rd|th)"};
1248     case "w":
1249         return {g:0,
1250             c:null,
1251             s:"\\d"};
1252     case "z":
1253         return {g:0,
1254             c:null,
1255             s:"(?:\\d{1,3})"};
1256     case "W":
1257         return {g:0,
1258             c:null,
1259             s:"(?:\\d{2})"};
1260     case "F":
1261         return {g:1,
1262             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1263             s:"(" + Date.monthNames.join("|") + ")"};
1264     case "M":
1265         return {g:1,
1266             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1267             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1268     case "n":
1269         return {g:1,
1270             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1271             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1272     case "m":
1273         return {g:1,
1274             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1275             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1276     case "t":
1277         return {g:0,
1278             c:null,
1279             s:"\\d{1,2}"};
1280     case "L":
1281         return {g:0,
1282             c:null,
1283             s:"(?:1|0)"};
1284     case "Y":
1285         return {g:1,
1286             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{4})"};
1288     case "y":
1289         return {g:1,
1290             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1291                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1292             s:"(\\d{1,2})"};
1293     case "a":
1294         return {g:1,
1295             c:"if (results[" + currentGroup + "] == 'am') {\n"
1296                 + "if (h == 12) { h = 0; }\n"
1297                 + "} else { if (h < 12) { h += 12; }}",
1298             s:"(am|pm)"};
1299     case "A":
1300         return {g:1,
1301             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1302                 + "if (h == 12) { h = 0; }\n"
1303                 + "} else { if (h < 12) { h += 12; }}",
1304             s:"(AM|PM)"};
1305     case "g":
1306     case "G":
1307         return {g:1,
1308             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1309             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1310     case "h":
1311     case "H":
1312         return {g:1,
1313             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1315     case "i":
1316         return {g:1,
1317             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1318             s:"(\\d{2})"};
1319     case "s":
1320         return {g:1,
1321             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1322             s:"(\\d{2})"};
1323     case "O":
1324         return {g:1,
1325             c:[
1326                 "o = results[", currentGroup, "];\n",
1327                 "var sn = o.substring(0,1);\n", // get + / - sign
1328                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1329                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1330                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1331                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1332             ].join(""),
1333             s:"([+\-]\\d{4})"};
1334     case "T":
1335         return {g:0,
1336             c:null,
1337             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1338     case "Z":
1339         return {g:1,
1340             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1341                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1342             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1343     default:
1344         return {g:0,
1345             c:null,
1346             s:String.escape(character)};
1347     }
1348 };
1349
1350 /**
1351  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1352  * @return {String} The abbreviated timezone name (e.g. 'CST')
1353  */
1354 Date.prototype.getTimezone = function() {
1355     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1356 };
1357
1358 /**
1359  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1360  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1361  */
1362 Date.prototype.getGMTOffset = function() {
1363     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1364         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1365         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1366 };
1367
1368 /**
1369  * Get the numeric day number of the year, adjusted for leap year.
1370  * @return {Number} 0 through 364 (365 in leap years)
1371  */
1372 Date.prototype.getDayOfYear = function() {
1373     var num = 0;
1374     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1375     for (var i = 0; i < this.getMonth(); ++i) {
1376         num += Date.daysInMonth[i];
1377     }
1378     return num + this.getDate() - 1;
1379 };
1380
1381 /**
1382  * Get the string representation of the numeric week number of the year
1383  * (equivalent to the format specifier 'W').
1384  * @return {String} '00' through '52'
1385  */
1386 Date.prototype.getWeekOfYear = function() {
1387     // Skip to Thursday of this week
1388     var now = this.getDayOfYear() + (4 - this.getDay());
1389     // Find the first Thursday of the year
1390     var jan1 = new Date(this.getFullYear(), 0, 1);
1391     var then = (7 - jan1.getDay() + 4);
1392     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1393 };
1394
1395 /**
1396  * Whether or not the current date is in a leap year.
1397  * @return {Boolean} True if the current date is in a leap year, else false
1398  */
1399 Date.prototype.isLeapYear = function() {
1400     var year = this.getFullYear();
1401     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1402 };
1403
1404 /**
1405  * Get the first day of the current month, adjusted for leap year.  The returned value
1406  * is the numeric day index within the week (0-6) which can be used in conjunction with
1407  * the {@link #monthNames} array to retrieve the textual day name.
1408  * Example:
1409  *<pre><code>
1410 var dt = new Date('1/10/2007');
1411 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1412 </code></pre>
1413  * @return {Number} The day number (0-6)
1414  */
1415 Date.prototype.getFirstDayOfMonth = function() {
1416     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1417     return (day < 0) ? (day + 7) : day;
1418 };
1419
1420 /**
1421  * Get the last day of the current month, adjusted for leap year.  The returned value
1422  * is the numeric day index within the week (0-6) which can be used in conjunction with
1423  * the {@link #monthNames} array to retrieve the textual day name.
1424  * Example:
1425  *<pre><code>
1426 var dt = new Date('1/10/2007');
1427 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1428 </code></pre>
1429  * @return {Number} The day number (0-6)
1430  */
1431 Date.prototype.getLastDayOfMonth = function() {
1432     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1433     return (day < 0) ? (day + 7) : day;
1434 };
1435
1436
1437 /**
1438  * Get the first date of this date's month
1439  * @return {Date}
1440  */
1441 Date.prototype.getFirstDateOfMonth = function() {
1442     return new Date(this.getFullYear(), this.getMonth(), 1);
1443 };
1444
1445 /**
1446  * Get the last date of this date's month
1447  * @return {Date}
1448  */
1449 Date.prototype.getLastDateOfMonth = function() {
1450     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1451 };
1452 /**
1453  * Get the number of days in the current month, adjusted for leap year.
1454  * @return {Number} The number of days in the month
1455  */
1456 Date.prototype.getDaysInMonth = function() {
1457     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1458     return Date.daysInMonth[this.getMonth()];
1459 };
1460
1461 /**
1462  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1463  * @return {String} 'st, 'nd', 'rd' or 'th'
1464  */
1465 Date.prototype.getSuffix = function() {
1466     switch (this.getDate()) {
1467         case 1:
1468         case 21:
1469         case 31:
1470             return "st";
1471         case 2:
1472         case 22:
1473             return "nd";
1474         case 3:
1475         case 23:
1476             return "rd";
1477         default:
1478             return "th";
1479     }
1480 };
1481
1482 // private
1483 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1484
1485 /**
1486  * An array of textual month names.
1487  * Override these values for international dates, for example...
1488  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1489  * @type Array
1490  * @static
1491  */
1492 Date.monthNames =
1493    ["January",
1494     "February",
1495     "March",
1496     "April",
1497     "May",
1498     "June",
1499     "July",
1500     "August",
1501     "September",
1502     "October",
1503     "November",
1504     "December"];
1505
1506 /**
1507  * An array of textual day names.
1508  * Override these values for international dates, for example...
1509  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1510  * @type Array
1511  * @static
1512  */
1513 Date.dayNames =
1514    ["Sunday",
1515     "Monday",
1516     "Tuesday",
1517     "Wednesday",
1518     "Thursday",
1519     "Friday",
1520     "Saturday"];
1521
1522 // private
1523 Date.y2kYear = 50;
1524 // private
1525 Date.monthNumbers = {
1526     Jan:0,
1527     Feb:1,
1528     Mar:2,
1529     Apr:3,
1530     May:4,
1531     Jun:5,
1532     Jul:6,
1533     Aug:7,
1534     Sep:8,
1535     Oct:9,
1536     Nov:10,
1537     Dec:11};
1538
1539 /**
1540  * Creates and returns a new Date instance with the exact same date value as the called instance.
1541  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1542  * variable will also be changed.  When the intention is to create a new variable that will not
1543  * modify the original instance, you should create a clone.
1544  *
1545  * Example of correctly cloning a date:
1546  * <pre><code>
1547 //wrong way:
1548 var orig = new Date('10/1/2006');
1549 var copy = orig;
1550 copy.setDate(5);
1551 document.write(orig);  //returns 'Thu Oct 05 2006'!
1552
1553 //correct way:
1554 var orig = new Date('10/1/2006');
1555 var copy = orig.clone();
1556 copy.setDate(5);
1557 document.write(orig);  //returns 'Thu Oct 01 2006'
1558 </code></pre>
1559  * @return {Date} The new Date instance
1560  */
1561 Date.prototype.clone = function() {
1562         return new Date(this.getTime());
1563 };
1564
1565 /**
1566  * Clears any time information from this date
1567  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1568  @return {Date} this or the clone
1569  */
1570 Date.prototype.clearTime = function(clone){
1571     if(clone){
1572         return this.clone().clearTime();
1573     }
1574     this.setHours(0);
1575     this.setMinutes(0);
1576     this.setSeconds(0);
1577     this.setMilliseconds(0);
1578     return this;
1579 };
1580
1581 // private
1582 // safari setMonth is broken
1583 if(Roo.isSafari){
1584     Date.brokenSetMonth = Date.prototype.setMonth;
1585         Date.prototype.setMonth = function(num){
1586                 if(num <= -1){
1587                         var n = Math.ceil(-num);
1588                         var back_year = Math.ceil(n/12);
1589                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1590                         this.setFullYear(this.getFullYear() - back_year);
1591                         return Date.brokenSetMonth.call(this, month);
1592                 } else {
1593                         return Date.brokenSetMonth.apply(this, arguments);
1594                 }
1595         };
1596 }
1597
1598 /** Date interval constant 
1599 * @static 
1600 * @type String */
1601 Date.MILLI = "ms";
1602 /** Date interval constant 
1603 * @static 
1604 * @type String */
1605 Date.SECOND = "s";
1606 /** Date interval constant 
1607 * @static 
1608 * @type String */
1609 Date.MINUTE = "mi";
1610 /** Date interval constant 
1611 * @static 
1612 * @type String */
1613 Date.HOUR = "h";
1614 /** Date interval constant 
1615 * @static 
1616 * @type String */
1617 Date.DAY = "d";
1618 /** Date interval constant 
1619 * @static 
1620 * @type String */
1621 Date.MONTH = "mo";
1622 /** Date interval constant 
1623 * @static 
1624 * @type String */
1625 Date.YEAR = "y";
1626
1627 /**
1628  * Provides a convenient method of performing basic date arithmetic.  This method
1629  * does not modify the Date instance being called - it creates and returns
1630  * a new Date instance containing the resulting date value.
1631  *
1632  * Examples:
1633  * <pre><code>
1634 //Basic usage:
1635 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1636 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1637
1638 //Negative values will subtract correctly:
1639 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1640 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1641
1642 //You can even chain several calls together in one line!
1643 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1644 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1645  </code></pre>
1646  *
1647  * @param {String} interval   A valid date interval enum value
1648  * @param {Number} value      The amount to add to the current date
1649  * @return {Date} The new Date instance
1650  */
1651 Date.prototype.add = function(interval, value){
1652   var d = this.clone();
1653   if (!interval || value === 0) return d;
1654   switch(interval.toLowerCase()){
1655     case Date.MILLI:
1656       d.setMilliseconds(this.getMilliseconds() + value);
1657       break;
1658     case Date.SECOND:
1659       d.setSeconds(this.getSeconds() + value);
1660       break;
1661     case Date.MINUTE:
1662       d.setMinutes(this.getMinutes() + value);
1663       break;
1664     case Date.HOUR:
1665       d.setHours(this.getHours() + value);
1666       break;
1667     case Date.DAY:
1668       d.setDate(this.getDate() + value);
1669       break;
1670     case Date.MONTH:
1671       var day = this.getDate();
1672       if(day > 28){
1673           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1674       }
1675       d.setDate(day);
1676       d.setMonth(this.getMonth() + value);
1677       break;
1678     case Date.YEAR:
1679       d.setFullYear(this.getFullYear() + value);
1680       break;
1681   }
1682   return d;
1683 };/*
1684  * Based on:
1685  * Ext JS Library 1.1.1
1686  * Copyright(c) 2006-2007, Ext JS, LLC.
1687  *
1688  * Originally Released Under LGPL - original licence link has changed is not relivant.
1689  *
1690  * Fork - LGPL
1691  * <script type="text/javascript">
1692  */
1693
1694 Roo.lib.Dom = {
1695     getViewWidth : function(full) {
1696         return full ? this.getDocumentWidth() : this.getViewportWidth();
1697     },
1698
1699     getViewHeight : function(full) {
1700         return full ? this.getDocumentHeight() : this.getViewportHeight();
1701     },
1702
1703     getDocumentHeight: function() {
1704         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1705         return Math.max(scrollHeight, this.getViewportHeight());
1706     },
1707
1708     getDocumentWidth: function() {
1709         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1710         return Math.max(scrollWidth, this.getViewportWidth());
1711     },
1712
1713     getViewportHeight: function() {
1714         var height = self.innerHeight;
1715         var mode = document.compatMode;
1716
1717         if ((mode || Roo.isIE) && !Roo.isOpera) {
1718             height = (mode == "CSS1Compat") ?
1719                      document.documentElement.clientHeight :
1720                      document.body.clientHeight;
1721         }
1722
1723         return height;
1724     },
1725
1726     getViewportWidth: function() {
1727         var width = self.innerWidth;
1728         var mode = document.compatMode;
1729
1730         if (mode || Roo.isIE) {
1731             width = (mode == "CSS1Compat") ?
1732                     document.documentElement.clientWidth :
1733                     document.body.clientWidth;
1734         }
1735         return width;
1736     },
1737
1738     isAncestor : function(p, c) {
1739         p = Roo.getDom(p);
1740         c = Roo.getDom(c);
1741         if (!p || !c) {
1742             return false;
1743         }
1744
1745         if (p.contains && !Roo.isSafari) {
1746             return p.contains(c);
1747         } else if (p.compareDocumentPosition) {
1748             return !!(p.compareDocumentPosition(c) & 16);
1749         } else {
1750             var parent = c.parentNode;
1751             while (parent) {
1752                 if (parent == p) {
1753                     return true;
1754                 }
1755                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1756                     return false;
1757                 }
1758                 parent = parent.parentNode;
1759             }
1760             return false;
1761         }
1762     },
1763
1764     getRegion : function(el) {
1765         return Roo.lib.Region.getRegion(el);
1766     },
1767
1768     getY : function(el) {
1769         return this.getXY(el)[1];
1770     },
1771
1772     getX : function(el) {
1773         return this.getXY(el)[0];
1774     },
1775
1776     getXY : function(el) {
1777         var p, pe, b, scroll, bd = document.body;
1778         el = Roo.getDom(el);
1779         var fly = Roo.lib.AnimBase.fly;
1780         if (el.getBoundingClientRect) {
1781             b = el.getBoundingClientRect();
1782             scroll = fly(document).getScroll();
1783             return [b.left + scroll.left, b.top + scroll.top];
1784         }
1785         var x = 0, y = 0;
1786
1787         p = el;
1788
1789         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1790
1791         while (p) {
1792
1793             x += p.offsetLeft;
1794             y += p.offsetTop;
1795
1796             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1797                 hasAbsolute = true;
1798             }
1799
1800             if (Roo.isGecko) {
1801                 pe = fly(p);
1802
1803                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1804                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1805
1806
1807                 x += bl;
1808                 y += bt;
1809
1810
1811                 if (p != el && pe.getStyle('overflow') != 'visible') {
1812                     x += bl;
1813                     y += bt;
1814                 }
1815             }
1816             p = p.offsetParent;
1817         }
1818
1819         if (Roo.isSafari && hasAbsolute) {
1820             x -= bd.offsetLeft;
1821             y -= bd.offsetTop;
1822         }
1823
1824         if (Roo.isGecko && !hasAbsolute) {
1825             var dbd = fly(bd);
1826             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1827             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1828         }
1829
1830         p = el.parentNode;
1831         while (p && p != bd) {
1832             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1833                 x -= p.scrollLeft;
1834                 y -= p.scrollTop;
1835             }
1836             p = p.parentNode;
1837         }
1838         return [x, y];
1839     },
1840  
1841   
1842
1843
1844     setXY : function(el, xy) {
1845         el = Roo.fly(el, '_setXY');
1846         el.position();
1847         var pts = el.translatePoints(xy);
1848         if (xy[0] !== false) {
1849             el.dom.style.left = pts.left + "px";
1850         }
1851         if (xy[1] !== false) {
1852             el.dom.style.top = pts.top + "px";
1853         }
1854     },
1855
1856     setX : function(el, x) {
1857         this.setXY(el, [x, false]);
1858     },
1859
1860     setY : function(el, y) {
1861         this.setXY(el, [false, y]);
1862     }
1863 };
1864 /*
1865  * Portions of this file are based on pieces of Yahoo User Interface Library
1866  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1867  * YUI licensed under the BSD License:
1868  * http://developer.yahoo.net/yui/license.txt
1869  * <script type="text/javascript">
1870  *
1871  */
1872
1873 Roo.lib.Event = function() {
1874     var loadComplete = false;
1875     var listeners = [];
1876     var unloadListeners = [];
1877     var retryCount = 0;
1878     var onAvailStack = [];
1879     var counter = 0;
1880     var lastError = null;
1881
1882     return {
1883         POLL_RETRYS: 200,
1884         POLL_INTERVAL: 20,
1885         EL: 0,
1886         TYPE: 1,
1887         FN: 2,
1888         WFN: 3,
1889         OBJ: 3,
1890         ADJ_SCOPE: 4,
1891         _interval: null,
1892
1893         startInterval: function() {
1894             if (!this._interval) {
1895                 var self = this;
1896                 var callback = function() {
1897                     self._tryPreloadAttach();
1898                 };
1899                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1900
1901             }
1902         },
1903
1904         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1905             onAvailStack.push({ id:         p_id,
1906                 fn:         p_fn,
1907                 obj:        p_obj,
1908                 override:   p_override,
1909                 checkReady: false    });
1910
1911             retryCount = this.POLL_RETRYS;
1912             this.startInterval();
1913         },
1914
1915
1916         addListener: function(el, eventName, fn) {
1917             el = Roo.getDom(el);
1918             if (!el || !fn) {
1919                 return false;
1920             }
1921
1922             if ("unload" == eventName) {
1923                 unloadListeners[unloadListeners.length] =
1924                 [el, eventName, fn];
1925                 return true;
1926             }
1927
1928             var wrappedFn = function(e) {
1929                 return fn(Roo.lib.Event.getEvent(e));
1930             };
1931
1932             var li = [el, eventName, fn, wrappedFn];
1933
1934             var index = listeners.length;
1935             listeners[index] = li;
1936
1937             this.doAdd(el, eventName, wrappedFn, false);
1938             return true;
1939
1940         },
1941
1942
1943         removeListener: function(el, eventName, fn) {
1944             var i, len;
1945
1946             el = Roo.getDom(el);
1947
1948             if(!fn) {
1949                 return this.purgeElement(el, false, eventName);
1950             }
1951
1952
1953             if ("unload" == eventName) {
1954
1955                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1956                     var li = unloadListeners[i];
1957                     if (li &&
1958                         li[0] == el &&
1959                         li[1] == eventName &&
1960                         li[2] == fn) {
1961                         unloadListeners.splice(i, 1);
1962                         return true;
1963                     }
1964                 }
1965
1966                 return false;
1967             }
1968
1969             var cacheItem = null;
1970
1971
1972             var index = arguments[3];
1973
1974             if ("undefined" == typeof index) {
1975                 index = this._getCacheIndex(el, eventName, fn);
1976             }
1977
1978             if (index >= 0) {
1979                 cacheItem = listeners[index];
1980             }
1981
1982             if (!el || !cacheItem) {
1983                 return false;
1984             }
1985
1986             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1987
1988             delete listeners[index][this.WFN];
1989             delete listeners[index][this.FN];
1990             listeners.splice(index, 1);
1991
1992             return true;
1993
1994         },
1995
1996
1997         getTarget: function(ev, resolveTextNode) {
1998             ev = ev.browserEvent || ev;
1999             var t = ev.target || ev.srcElement;
2000             return this.resolveTextNode(t);
2001         },
2002
2003
2004         resolveTextNode: function(node) {
2005             if (Roo.isSafari && node && 3 == node.nodeType) {
2006                 return node.parentNode;
2007             } else {
2008                 return node;
2009             }
2010         },
2011
2012
2013         getPageX: function(ev) {
2014             ev = ev.browserEvent || ev;
2015             var x = ev.pageX;
2016             if (!x && 0 !== x) {
2017                 x = ev.clientX || 0;
2018
2019                 if (Roo.isIE) {
2020                     x += this.getScroll()[1];
2021                 }
2022             }
2023
2024             return x;
2025         },
2026
2027
2028         getPageY: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             var y = ev.pageY;
2031             if (!y && 0 !== y) {
2032                 y = ev.clientY || 0;
2033
2034                 if (Roo.isIE) {
2035                     y += this.getScroll()[0];
2036                 }
2037             }
2038
2039
2040             return y;
2041         },
2042
2043
2044         getXY: function(ev) {
2045             ev = ev.browserEvent || ev;
2046             return [this.getPageX(ev), this.getPageY(ev)];
2047         },
2048
2049
2050         getRelatedTarget: function(ev) {
2051             ev = ev.browserEvent || ev;
2052             var t = ev.relatedTarget;
2053             if (!t) {
2054                 if (ev.type == "mouseout") {
2055                     t = ev.toElement;
2056                 } else if (ev.type == "mouseover") {
2057                     t = ev.fromElement;
2058                 }
2059             }
2060
2061             return this.resolveTextNode(t);
2062         },
2063
2064
2065         getTime: function(ev) {
2066             ev = ev.browserEvent || ev;
2067             if (!ev.time) {
2068                 var t = new Date().getTime();
2069                 try {
2070                     ev.time = t;
2071                 } catch(ex) {
2072                     this.lastError = ex;
2073                     return t;
2074                 }
2075             }
2076
2077             return ev.time;
2078         },
2079
2080
2081         stopEvent: function(ev) {
2082             this.stopPropagation(ev);
2083             this.preventDefault(ev);
2084         },
2085
2086
2087         stopPropagation: function(ev) {
2088             ev = ev.browserEvent || ev;
2089             if (ev.stopPropagation) {
2090                 ev.stopPropagation();
2091             } else {
2092                 ev.cancelBubble = true;
2093             }
2094         },
2095
2096
2097         preventDefault: function(ev) {
2098             ev = ev.browserEvent || ev;
2099             if(ev.preventDefault) {
2100                 ev.preventDefault();
2101             } else {
2102                 ev.returnValue = false;
2103             }
2104         },
2105
2106
2107         getEvent: function(e) {
2108             var ev = e || window.event;
2109             if (!ev) {
2110                 var c = this.getEvent.caller;
2111                 while (c) {
2112                     ev = c.arguments[0];
2113                     if (ev && Event == ev.constructor) {
2114                         break;
2115                     }
2116                     c = c.caller;
2117                 }
2118             }
2119             return ev;
2120         },
2121
2122
2123         getCharCode: function(ev) {
2124             ev = ev.browserEvent || ev;
2125             return ev.charCode || ev.keyCode || 0;
2126         },
2127
2128
2129         _getCacheIndex: function(el, eventName, fn) {
2130             for (var i = 0,len = listeners.length; i < len; ++i) {
2131                 var li = listeners[i];
2132                 if (li &&
2133                     li[this.FN] == fn &&
2134                     li[this.EL] == el &&
2135                     li[this.TYPE] == eventName) {
2136                     return i;
2137                 }
2138             }
2139
2140             return -1;
2141         },
2142
2143
2144         elCache: {},
2145
2146
2147         getEl: function(id) {
2148             return document.getElementById(id);
2149         },
2150
2151
2152         clearCache: function() {
2153         },
2154
2155
2156         _load: function(e) {
2157             loadComplete = true;
2158             var EU = Roo.lib.Event;
2159
2160
2161             if (Roo.isIE) {
2162                 EU.doRemove(window, "load", EU._load);
2163             }
2164         },
2165
2166
2167         _tryPreloadAttach: function() {
2168
2169             if (this.locked) {
2170                 return false;
2171             }
2172
2173             this.locked = true;
2174
2175
2176             var tryAgain = !loadComplete;
2177             if (!tryAgain) {
2178                 tryAgain = (retryCount > 0);
2179             }
2180
2181
2182             var notAvail = [];
2183             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2184                 var item = onAvailStack[i];
2185                 if (item) {
2186                     var el = this.getEl(item.id);
2187
2188                     if (el) {
2189                         if (!item.checkReady ||
2190                             loadComplete ||
2191                             el.nextSibling ||
2192                             (document && document.body)) {
2193
2194                             var scope = el;
2195                             if (item.override) {
2196                                 if (item.override === true) {
2197                                     scope = item.obj;
2198                                 } else {
2199                                     scope = item.override;
2200                                 }
2201                             }
2202                             item.fn.call(scope, item.obj);
2203                             onAvailStack[i] = null;
2204                         }
2205                     } else {
2206                         notAvail.push(item);
2207                     }
2208                 }
2209             }
2210
2211             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2212
2213             if (tryAgain) {
2214
2215                 this.startInterval();
2216             } else {
2217                 clearInterval(this._interval);
2218                 this._interval = null;
2219             }
2220
2221             this.locked = false;
2222
2223             return true;
2224
2225         },
2226
2227
2228         purgeElement: function(el, recurse, eventName) {
2229             var elListeners = this.getListeners(el, eventName);
2230             if (elListeners) {
2231                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2232                     var l = elListeners[i];
2233                     this.removeListener(el, l.type, l.fn);
2234                 }
2235             }
2236
2237             if (recurse && el && el.childNodes) {
2238                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2239                     this.purgeElement(el.childNodes[i], recurse, eventName);
2240                 }
2241             }
2242         },
2243
2244
2245         getListeners: function(el, eventName) {
2246             var results = [], searchLists;
2247             if (!eventName) {
2248                 searchLists = [listeners, unloadListeners];
2249             } else if (eventName == "unload") {
2250                 searchLists = [unloadListeners];
2251             } else {
2252                 searchLists = [listeners];
2253             }
2254
2255             for (var j = 0; j < searchLists.length; ++j) {
2256                 var searchList = searchLists[j];
2257                 if (searchList && searchList.length > 0) {
2258                     for (var i = 0,len = searchList.length; i < len; ++i) {
2259                         var l = searchList[i];
2260                         if (l && l[this.EL] === el &&
2261                             (!eventName || eventName === l[this.TYPE])) {
2262                             results.push({
2263                                 type:   l[this.TYPE],
2264                                 fn:     l[this.FN],
2265                                 obj:    l[this.OBJ],
2266                                 adjust: l[this.ADJ_SCOPE],
2267                                 index:  i
2268                             });
2269                         }
2270                     }
2271                 }
2272             }
2273
2274             return (results.length) ? results : null;
2275         },
2276
2277
2278         _unload: function(e) {
2279
2280             var EU = Roo.lib.Event, i, j, l, len, index;
2281
2282             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2283                 l = unloadListeners[i];
2284                 if (l) {
2285                     var scope = window;
2286                     if (l[EU.ADJ_SCOPE]) {
2287                         if (l[EU.ADJ_SCOPE] === true) {
2288                             scope = l[EU.OBJ];
2289                         } else {
2290                             scope = l[EU.ADJ_SCOPE];
2291                         }
2292                     }
2293                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2294                     unloadListeners[i] = null;
2295                     l = null;
2296                     scope = null;
2297                 }
2298             }
2299
2300             unloadListeners = null;
2301
2302             if (listeners && listeners.length > 0) {
2303                 j = listeners.length;
2304                 while (j) {
2305                     index = j - 1;
2306                     l = listeners[index];
2307                     if (l) {
2308                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2309                                 l[EU.FN], index);
2310                     }
2311                     j = j - 1;
2312                 }
2313                 l = null;
2314
2315                 EU.clearCache();
2316             }
2317
2318             EU.doRemove(window, "unload", EU._unload);
2319
2320         },
2321
2322
2323         getScroll: function() {
2324             var dd = document.documentElement, db = document.body;
2325             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2326                 return [dd.scrollTop, dd.scrollLeft];
2327             } else if (db) {
2328                 return [db.scrollTop, db.scrollLeft];
2329             } else {
2330                 return [0, 0];
2331             }
2332         },
2333
2334
2335         doAdd: function () {
2336             if (window.addEventListener) {
2337                 return function(el, eventName, fn, capture) {
2338                     el.addEventListener(eventName, fn, (capture));
2339                 };
2340             } else if (window.attachEvent) {
2341                 return function(el, eventName, fn, capture) {
2342                     el.attachEvent("on" + eventName, fn);
2343                 };
2344             } else {
2345                 return function() {
2346                 };
2347             }
2348         }(),
2349
2350
2351         doRemove: function() {
2352             if (window.removeEventListener) {
2353                 return function (el, eventName, fn, capture) {
2354                     el.removeEventListener(eventName, fn, (capture));
2355                 };
2356             } else if (window.detachEvent) {
2357                 return function (el, eventName, fn) {
2358                     el.detachEvent("on" + eventName, fn);
2359                 };
2360             } else {
2361                 return function() {
2362                 };
2363             }
2364         }()
2365     };
2366     
2367 }();
2368 (function() {     
2369    
2370     var E = Roo.lib.Event;
2371     E.on = E.addListener;
2372     E.un = E.removeListener;
2373
2374     if (document && document.body) {
2375         E._load();
2376     } else {
2377         E.doAdd(window, "load", E._load);
2378     }
2379     E.doAdd(window, "unload", E._unload);
2380     E._tryPreloadAttach();
2381 })();
2382
2383 /*
2384  * Portions of this file are based on pieces of Yahoo User Interface Library
2385  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2386  * YUI licensed under the BSD License:
2387  * http://developer.yahoo.net/yui/license.txt
2388  * <script type="text/javascript">
2389  *
2390  */
2391
2392 (function() {
2393     
2394     Roo.lib.Ajax = {
2395         request : function(method, uri, cb, data, options) {
2396             if(options){
2397                 var hs = options.headers;
2398                 if(hs){
2399                     for(var h in hs){
2400                         if(hs.hasOwnProperty(h)){
2401                             this.initHeader(h, hs[h], false);
2402                         }
2403                     }
2404                 }
2405                 if(options.xmlData){
2406                     this.initHeader('Content-Type', 'text/xml', false);
2407                     method = 'POST';
2408                     data = options.xmlData;
2409                 }
2410             }
2411
2412             return this.asyncRequest(method, uri, cb, data);
2413         },
2414
2415         serializeForm : function(form) {
2416             if(typeof form == 'string') {
2417                 form = (document.getElementById(form) || document.forms[form]);
2418             }
2419
2420             var el, name, val, disabled, data = '', hasSubmit = false;
2421             for (var i = 0; i < form.elements.length; i++) {
2422                 el = form.elements[i];
2423                 disabled = form.elements[i].disabled;
2424                 name = form.elements[i].name;
2425                 val = form.elements[i].value;
2426
2427                 if (!disabled && name){
2428                     switch (el.type)
2429                             {
2430                         case 'select-one':
2431                         case 'select-multiple':
2432                             for (var j = 0; j < el.options.length; j++) {
2433                                 if (el.options[j].selected) {
2434                                     if (Roo.isIE) {
2435                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2436                                     }
2437                                     else {
2438                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2439                                     }
2440                                 }
2441                             }
2442                             break;
2443                         case 'radio':
2444                         case 'checkbox':
2445                             if (el.checked) {
2446                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2447                             }
2448                             break;
2449                         case 'file':
2450
2451                         case undefined:
2452
2453                         case 'reset':
2454
2455                         case 'button':
2456
2457                             break;
2458                         case 'submit':
2459                             if(hasSubmit == false) {
2460                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2461                                 hasSubmit = true;
2462                             }
2463                             break;
2464                         default:
2465                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2466                             break;
2467                     }
2468                 }
2469             }
2470             data = data.substr(0, data.length - 1);
2471             return data;
2472         },
2473
2474         headers:{},
2475
2476         hasHeaders:false,
2477
2478         useDefaultHeader:true,
2479
2480         defaultPostHeader:'application/x-www-form-urlencoded',
2481
2482         useDefaultXhrHeader:true,
2483
2484         defaultXhrHeader:'XMLHttpRequest',
2485
2486         hasDefaultHeaders:true,
2487
2488         defaultHeaders:{},
2489
2490         poll:{},
2491
2492         timeout:{},
2493
2494         pollInterval:50,
2495
2496         transactionId:0,
2497
2498         setProgId:function(id)
2499         {
2500             this.activeX.unshift(id);
2501         },
2502
2503         setDefaultPostHeader:function(b)
2504         {
2505             this.useDefaultHeader = b;
2506         },
2507
2508         setDefaultXhrHeader:function(b)
2509         {
2510             this.useDefaultXhrHeader = b;
2511         },
2512
2513         setPollingInterval:function(i)
2514         {
2515             if (typeof i == 'number' && isFinite(i)) {
2516                 this.pollInterval = i;
2517             }
2518         },
2519
2520         createXhrObject:function(transactionId)
2521         {
2522             var obj,http;
2523             try
2524             {
2525
2526                 http = new XMLHttpRequest();
2527
2528                 obj = { conn:http, tId:transactionId };
2529             }
2530             catch(e)
2531             {
2532                 for (var i = 0; i < this.activeX.length; ++i) {
2533                     try
2534                     {
2535
2536                         http = new ActiveXObject(this.activeX[i]);
2537
2538                         obj = { conn:http, tId:transactionId };
2539                         break;
2540                     }
2541                     catch(e) {
2542                     }
2543                 }
2544             }
2545             finally
2546             {
2547                 return obj;
2548             }
2549         },
2550
2551         getConnectionObject:function()
2552         {
2553             var o;
2554             var tId = this.transactionId;
2555
2556             try
2557             {
2558                 o = this.createXhrObject(tId);
2559                 if (o) {
2560                     this.transactionId++;
2561                 }
2562             }
2563             catch(e) {
2564             }
2565             finally
2566             {
2567                 return o;
2568             }
2569         },
2570
2571         asyncRequest:function(method, uri, callback, postData)
2572         {
2573             var o = this.getConnectionObject();
2574
2575             if (!o) {
2576                 return null;
2577             }
2578             else {
2579                 o.conn.open(method, uri, true);
2580
2581                 if (this.useDefaultXhrHeader) {
2582                     if (!this.defaultHeaders['X-Requested-With']) {
2583                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2584                     }
2585                 }
2586
2587                 if(postData && this.useDefaultHeader){
2588                     this.initHeader('Content-Type', this.defaultPostHeader);
2589                 }
2590
2591                  if (this.hasDefaultHeaders || this.hasHeaders) {
2592                     this.setHeader(o);
2593                 }
2594
2595                 this.handleReadyState(o, callback);
2596                 o.conn.send(postData || null);
2597
2598                 return o;
2599             }
2600         },
2601
2602         handleReadyState:function(o, callback)
2603         {
2604             var oConn = this;
2605
2606             if (callback && callback.timeout) {
2607                 this.timeout[o.tId] = window.setTimeout(function() {
2608                     oConn.abort(o, callback, true);
2609                 }, callback.timeout);
2610             }
2611
2612             this.poll[o.tId] = window.setInterval(
2613                     function() {
2614                         if (o.conn && o.conn.readyState == 4) {
2615                             window.clearInterval(oConn.poll[o.tId]);
2616                             delete oConn.poll[o.tId];
2617
2618                             if(callback && callback.timeout) {
2619                                 window.clearTimeout(oConn.timeout[o.tId]);
2620                                 delete oConn.timeout[o.tId];
2621                             }
2622
2623                             oConn.handleTransactionResponse(o, callback);
2624                         }
2625                     }
2626                     , this.pollInterval);
2627         },
2628
2629         handleTransactionResponse:function(o, callback, isAbort)
2630         {
2631
2632             if (!callback) {
2633                 this.releaseObject(o);
2634                 return;
2635             }
2636
2637             var httpStatus, responseObject;
2638
2639             try
2640             {
2641                 if (o.conn.status !== undefined && o.conn.status != 0) {
2642                     httpStatus = o.conn.status;
2643                 }
2644                 else {
2645                     httpStatus = 13030;
2646                 }
2647             }
2648             catch(e) {
2649
2650
2651                 httpStatus = 13030;
2652             }
2653
2654             if (httpStatus >= 200 && httpStatus < 300) {
2655                 responseObject = this.createResponseObject(o, callback.argument);
2656                 if (callback.success) {
2657                     if (!callback.scope) {
2658                         callback.success(responseObject);
2659                     }
2660                     else {
2661
2662
2663                         callback.success.apply(callback.scope, [responseObject]);
2664                     }
2665                 }
2666             }
2667             else {
2668                 switch (httpStatus) {
2669
2670                     case 12002:
2671                     case 12029:
2672                     case 12030:
2673                     case 12031:
2674                     case 12152:
2675                     case 13030:
2676                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2677                         if (callback.failure) {
2678                             if (!callback.scope) {
2679                                 callback.failure(responseObject);
2680                             }
2681                             else {
2682                                 callback.failure.apply(callback.scope, [responseObject]);
2683                             }
2684                         }
2685                         break;
2686                     default:
2687                         responseObject = this.createResponseObject(o, callback.argument);
2688                         if (callback.failure) {
2689                             if (!callback.scope) {
2690                                 callback.failure(responseObject);
2691                             }
2692                             else {
2693                                 callback.failure.apply(callback.scope, [responseObject]);
2694                             }
2695                         }
2696                 }
2697             }
2698
2699             this.releaseObject(o);
2700             responseObject = null;
2701         },
2702
2703         createResponseObject:function(o, callbackArg)
2704         {
2705             var obj = {};
2706             var headerObj = {};
2707
2708             try
2709             {
2710                 var headerStr = o.conn.getAllResponseHeaders();
2711                 var header = headerStr.split('\n');
2712                 for (var i = 0; i < header.length; i++) {
2713                     var delimitPos = header[i].indexOf(':');
2714                     if (delimitPos != -1) {
2715                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2716                     }
2717                 }
2718             }
2719             catch(e) {
2720             }
2721
2722             obj.tId = o.tId;
2723             obj.status = o.conn.status;
2724             obj.statusText = o.conn.statusText;
2725             obj.getResponseHeader = headerObj;
2726             obj.getAllResponseHeaders = headerStr;
2727             obj.responseText = o.conn.responseText;
2728             obj.responseXML = o.conn.responseXML;
2729
2730             if (typeof callbackArg !== undefined) {
2731                 obj.argument = callbackArg;
2732             }
2733
2734             return obj;
2735         },
2736
2737         createExceptionObject:function(tId, callbackArg, isAbort)
2738         {
2739             var COMM_CODE = 0;
2740             var COMM_ERROR = 'communication failure';
2741             var ABORT_CODE = -1;
2742             var ABORT_ERROR = 'transaction aborted';
2743
2744             var obj = {};
2745
2746             obj.tId = tId;
2747             if (isAbort) {
2748                 obj.status = ABORT_CODE;
2749                 obj.statusText = ABORT_ERROR;
2750             }
2751             else {
2752                 obj.status = COMM_CODE;
2753                 obj.statusText = COMM_ERROR;
2754             }
2755
2756             if (callbackArg) {
2757                 obj.argument = callbackArg;
2758             }
2759
2760             return obj;
2761         },
2762
2763         initHeader:function(label, value, isDefault)
2764         {
2765             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2766
2767             if (headerObj[label] === undefined) {
2768                 headerObj[label] = value;
2769             }
2770             else {
2771
2772
2773                 headerObj[label] = value + "," + headerObj[label];
2774             }
2775
2776             if (isDefault) {
2777                 this.hasDefaultHeaders = true;
2778             }
2779             else {
2780                 this.hasHeaders = true;
2781             }
2782         },
2783
2784
2785         setHeader:function(o)
2786         {
2787             if (this.hasDefaultHeaders) {
2788                 for (var prop in this.defaultHeaders) {
2789                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2790                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2791                     }
2792                 }
2793             }
2794
2795             if (this.hasHeaders) {
2796                 for (var prop in this.headers) {
2797                     if (this.headers.hasOwnProperty(prop)) {
2798                         o.conn.setRequestHeader(prop, this.headers[prop]);
2799                     }
2800                 }
2801                 this.headers = {};
2802                 this.hasHeaders = false;
2803             }
2804         },
2805
2806         resetDefaultHeaders:function() {
2807             delete this.defaultHeaders;
2808             this.defaultHeaders = {};
2809             this.hasDefaultHeaders = false;
2810         },
2811
2812         abort:function(o, callback, isTimeout)
2813         {
2814             if(this.isCallInProgress(o)) {
2815                 o.conn.abort();
2816                 window.clearInterval(this.poll[o.tId]);
2817                 delete this.poll[o.tId];
2818                 if (isTimeout) {
2819                     delete this.timeout[o.tId];
2820                 }
2821
2822                 this.handleTransactionResponse(o, callback, true);
2823
2824                 return true;
2825             }
2826             else {
2827                 return false;
2828             }
2829         },
2830
2831
2832         isCallInProgress:function(o)
2833         {
2834             if (o && o.conn) {
2835                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2836             }
2837             else {
2838
2839                 return false;
2840             }
2841         },
2842
2843
2844         releaseObject:function(o)
2845         {
2846
2847             o.conn = null;
2848
2849             o = null;
2850         },
2851
2852         activeX:[
2853         'MSXML2.XMLHTTP.3.0',
2854         'MSXML2.XMLHTTP',
2855         'Microsoft.XMLHTTP'
2856         ]
2857
2858
2859     };
2860 })();/*
2861  * Portions of this file are based on pieces of Yahoo User Interface Library
2862  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2863  * YUI licensed under the BSD License:
2864  * http://developer.yahoo.net/yui/license.txt
2865  * <script type="text/javascript">
2866  *
2867  */
2868
2869 Roo.lib.Region = function(t, r, b, l) {
2870     this.top = t;
2871     this[1] = t;
2872     this.right = r;
2873     this.bottom = b;
2874     this.left = l;
2875     this[0] = l;
2876 };
2877
2878
2879 Roo.lib.Region.prototype = {
2880     contains : function(region) {
2881         return ( region.left >= this.left &&
2882                  region.right <= this.right &&
2883                  region.top >= this.top &&
2884                  region.bottom <= this.bottom    );
2885
2886     },
2887
2888     getArea : function() {
2889         return ( (this.bottom - this.top) * (this.right - this.left) );
2890     },
2891
2892     intersect : function(region) {
2893         var t = Math.max(this.top, region.top);
2894         var r = Math.min(this.right, region.right);
2895         var b = Math.min(this.bottom, region.bottom);
2896         var l = Math.max(this.left, region.left);
2897
2898         if (b >= t && r >= l) {
2899             return new Roo.lib.Region(t, r, b, l);
2900         } else {
2901             return null;
2902         }
2903     },
2904     union : function(region) {
2905         var t = Math.min(this.top, region.top);
2906         var r = Math.max(this.right, region.right);
2907         var b = Math.max(this.bottom, region.bottom);
2908         var l = Math.min(this.left, region.left);
2909
2910         return new Roo.lib.Region(t, r, b, l);
2911     },
2912
2913     adjust : function(t, l, b, r) {
2914         this.top += t;
2915         this.left += l;
2916         this.right += r;
2917         this.bottom += b;
2918         return this;
2919     }
2920 };
2921
2922 Roo.lib.Region.getRegion = function(el) {
2923     var p = Roo.lib.Dom.getXY(el);
2924
2925     var t = p[1];
2926     var r = p[0] + el.offsetWidth;
2927     var b = p[1] + el.offsetHeight;
2928     var l = p[0];
2929
2930     return new Roo.lib.Region(t, r, b, l);
2931 };
2932 /*
2933  * Portions of this file are based on pieces of Yahoo User Interface Library
2934  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2935  * YUI licensed under the BSD License:
2936  * http://developer.yahoo.net/yui/license.txt
2937  * <script type="text/javascript">
2938  *
2939  */
2940 //@@dep Roo.lib.Region
2941
2942
2943 Roo.lib.Point = function(x, y) {
2944     if (x instanceof Array) {
2945         y = x[1];
2946         x = x[0];
2947     }
2948     this.x = this.right = this.left = this[0] = x;
2949     this.y = this.top = this.bottom = this[1] = y;
2950 };
2951
2952 Roo.lib.Point.prototype = new Roo.lib.Region();
2953 /*
2954  * Portions of this file are based on pieces of Yahoo User Interface Library
2955  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2956  * YUI licensed under the BSD License:
2957  * http://developer.yahoo.net/yui/license.txt
2958  * <script type="text/javascript">
2959  *
2960  */
2961  
2962 (function() {   
2963
2964     Roo.lib.Anim = {
2965         scroll : function(el, args, duration, easing, cb, scope) {
2966             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2967         },
2968
2969         motion : function(el, args, duration, easing, cb, scope) {
2970             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2971         },
2972
2973         color : function(el, args, duration, easing, cb, scope) {
2974             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2975         },
2976
2977         run : function(el, args, duration, easing, cb, scope, type) {
2978             type = type || Roo.lib.AnimBase;
2979             if (typeof easing == "string") {
2980                 easing = Roo.lib.Easing[easing];
2981             }
2982             var anim = new type(el, args, duration, easing);
2983             anim.animateX(function() {
2984                 Roo.callback(cb, scope);
2985             });
2986             return anim;
2987         }
2988     };
2989 })();/*
2990  * Portions of this file are based on pieces of Yahoo User Interface Library
2991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2992  * YUI licensed under the BSD License:
2993  * http://developer.yahoo.net/yui/license.txt
2994  * <script type="text/javascript">
2995  *
2996  */
2997
2998 (function() {    
2999     var libFlyweight;
3000     
3001     function fly(el) {
3002         if (!libFlyweight) {
3003             libFlyweight = new Roo.Element.Flyweight();
3004         }
3005         libFlyweight.dom = el;
3006         return libFlyweight;
3007     }
3008
3009     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3010     
3011    
3012     
3013     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3014         if (el) {
3015             this.init(el, attributes, duration, method);
3016         }
3017     };
3018
3019     Roo.lib.AnimBase.fly = fly;
3020     
3021     
3022     
3023     Roo.lib.AnimBase.prototype = {
3024
3025         toString: function() {
3026             var el = this.getEl();
3027             var id = el.id || el.tagName;
3028             return ("Anim " + id);
3029         },
3030
3031         patterns: {
3032             noNegatives:        /width|height|opacity|padding/i,
3033             offsetAttribute:  /^((width|height)|(top|left))$/,
3034             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3035             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3036         },
3037
3038
3039         doMethod: function(attr, start, end) {
3040             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3041         },
3042
3043
3044         setAttribute: function(attr, val, unit) {
3045             if (this.patterns.noNegatives.test(attr)) {
3046                 val = (val > 0) ? val : 0;
3047             }
3048
3049             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3050         },
3051
3052
3053         getAttribute: function(attr) {
3054             var el = this.getEl();
3055             var val = fly(el).getStyle(attr);
3056
3057             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3058                 return parseFloat(val);
3059             }
3060
3061             var a = this.patterns.offsetAttribute.exec(attr) || [];
3062             var pos = !!( a[3] );
3063             var box = !!( a[2] );
3064
3065
3066             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3067                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3068             } else {
3069                 val = 0;
3070             }
3071
3072             return val;
3073         },
3074
3075
3076         getDefaultUnit: function(attr) {
3077             if (this.patterns.defaultUnit.test(attr)) {
3078                 return 'px';
3079             }
3080
3081             return '';
3082         },
3083
3084         animateX : function(callback, scope) {
3085             var f = function() {
3086                 this.onComplete.removeListener(f);
3087                 if (typeof callback == "function") {
3088                     callback.call(scope || this, this);
3089                 }
3090             };
3091             this.onComplete.addListener(f, this);
3092             this.animate();
3093         },
3094
3095
3096         setRuntimeAttribute: function(attr) {
3097             var start;
3098             var end;
3099             var attributes = this.attributes;
3100
3101             this.runtimeAttributes[attr] = {};
3102
3103             var isset = function(prop) {
3104                 return (typeof prop !== 'undefined');
3105             };
3106
3107             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3108                 return false;
3109             }
3110
3111             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3112
3113
3114             if (isset(attributes[attr]['to'])) {
3115                 end = attributes[attr]['to'];
3116             } else if (isset(attributes[attr]['by'])) {
3117                 if (start.constructor == Array) {
3118                     end = [];
3119                     for (var i = 0, len = start.length; i < len; ++i) {
3120                         end[i] = start[i] + attributes[attr]['by'][i];
3121                     }
3122                 } else {
3123                     end = start + attributes[attr]['by'];
3124                 }
3125             }
3126
3127             this.runtimeAttributes[attr].start = start;
3128             this.runtimeAttributes[attr].end = end;
3129
3130
3131             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3132         },
3133
3134
3135         init: function(el, attributes, duration, method) {
3136
3137             var isAnimated = false;
3138
3139
3140             var startTime = null;
3141
3142
3143             var actualFrames = 0;
3144
3145
3146             el = Roo.getDom(el);
3147
3148
3149             this.attributes = attributes || {};
3150
3151
3152             this.duration = duration || 1;
3153
3154
3155             this.method = method || Roo.lib.Easing.easeNone;
3156
3157
3158             this.useSeconds = true;
3159
3160
3161             this.currentFrame = 0;
3162
3163
3164             this.totalFrames = Roo.lib.AnimMgr.fps;
3165
3166
3167             this.getEl = function() {
3168                 return el;
3169             };
3170
3171
3172             this.isAnimated = function() {
3173                 return isAnimated;
3174             };
3175
3176
3177             this.getStartTime = function() {
3178                 return startTime;
3179             };
3180
3181             this.runtimeAttributes = {};
3182
3183
3184             this.animate = function() {
3185                 if (this.isAnimated()) {
3186                     return false;
3187                 }
3188
3189                 this.currentFrame = 0;
3190
3191                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3192
3193                 Roo.lib.AnimMgr.registerElement(this);
3194             };
3195
3196
3197             this.stop = function(finish) {
3198                 if (finish) {
3199                     this.currentFrame = this.totalFrames;
3200                     this._onTween.fire();
3201                 }
3202                 Roo.lib.AnimMgr.stop(this);
3203             };
3204
3205             var onStart = function() {
3206                 this.onStart.fire();
3207
3208                 this.runtimeAttributes = {};
3209                 for (var attr in this.attributes) {
3210                     this.setRuntimeAttribute(attr);
3211                 }
3212
3213                 isAnimated = true;
3214                 actualFrames = 0;
3215                 startTime = new Date();
3216             };
3217
3218
3219             var onTween = function() {
3220                 var data = {
3221                     duration: new Date() - this.getStartTime(),
3222                     currentFrame: this.currentFrame
3223                 };
3224
3225                 data.toString = function() {
3226                     return (
3227                             'duration: ' + data.duration +
3228                             ', currentFrame: ' + data.currentFrame
3229                             );
3230                 };
3231
3232                 this.onTween.fire(data);
3233
3234                 var runtimeAttributes = this.runtimeAttributes;
3235
3236                 for (var attr in runtimeAttributes) {
3237                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3238                 }
3239
3240                 actualFrames += 1;
3241             };
3242
3243             var onComplete = function() {
3244                 var actual_duration = (new Date() - startTime) / 1000 ;
3245
3246                 var data = {
3247                     duration: actual_duration,
3248                     frames: actualFrames,
3249                     fps: actualFrames / actual_duration
3250                 };
3251
3252                 data.toString = function() {
3253                     return (
3254                             'duration: ' + data.duration +
3255                             ', frames: ' + data.frames +
3256                             ', fps: ' + data.fps
3257                             );
3258                 };
3259
3260                 isAnimated = false;
3261                 actualFrames = 0;
3262                 this.onComplete.fire(data);
3263             };
3264
3265
3266             this._onStart = new Roo.util.Event(this);
3267             this.onStart = new Roo.util.Event(this);
3268             this.onTween = new Roo.util.Event(this);
3269             this._onTween = new Roo.util.Event(this);
3270             this.onComplete = new Roo.util.Event(this);
3271             this._onComplete = new Roo.util.Event(this);
3272             this._onStart.addListener(onStart);
3273             this._onTween.addListener(onTween);
3274             this._onComplete.addListener(onComplete);
3275         }
3276     };
3277 })();
3278 /*
3279  * Portions of this file are based on pieces of Yahoo User Interface Library
3280  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3281  * YUI licensed under the BSD License:
3282  * http://developer.yahoo.net/yui/license.txt
3283  * <script type="text/javascript">
3284  *
3285  */
3286
3287 Roo.lib.AnimMgr = new function() {
3288
3289         var thread = null;
3290
3291
3292         var queue = [];
3293
3294
3295         var tweenCount = 0;
3296
3297
3298         this.fps = 1000;
3299
3300
3301         this.delay = 1;
3302
3303
3304         this.registerElement = function(tween) {
3305             queue[queue.length] = tween;
3306             tweenCount += 1;
3307             tween._onStart.fire();
3308             this.start();
3309         };
3310
3311
3312         this.unRegister = function(tween, index) {
3313             tween._onComplete.fire();
3314             index = index || getIndex(tween);
3315             if (index != -1) {
3316                 queue.splice(index, 1);
3317             }
3318
3319             tweenCount -= 1;
3320             if (tweenCount <= 0) {
3321                 this.stop();
3322             }
3323         };
3324
3325
3326         this.start = function() {
3327             if (thread === null) {
3328                 thread = setInterval(this.run, this.delay);
3329             }
3330         };
3331
3332
3333         this.stop = function(tween) {
3334             if (!tween) {
3335                 clearInterval(thread);
3336
3337                 for (var i = 0, len = queue.length; i < len; ++i) {
3338                     if (queue[0].isAnimated()) {
3339                         this.unRegister(queue[0], 0);
3340                     }
3341                 }
3342
3343                 queue = [];
3344                 thread = null;
3345                 tweenCount = 0;
3346             }
3347             else {
3348                 this.unRegister(tween);
3349             }
3350         };
3351
3352
3353         this.run = function() {
3354             for (var i = 0, len = queue.length; i < len; ++i) {
3355                 var tween = queue[i];
3356                 if (!tween || !tween.isAnimated()) {
3357                     continue;
3358                 }
3359
3360                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3361                 {
3362                     tween.currentFrame += 1;
3363
3364                     if (tween.useSeconds) {
3365                         correctFrame(tween);
3366                     }
3367                     tween._onTween.fire();
3368                 }
3369                 else {
3370                     Roo.lib.AnimMgr.stop(tween, i);
3371                 }
3372             }
3373         };
3374
3375         var getIndex = function(anim) {
3376             for (var i = 0, len = queue.length; i < len; ++i) {
3377                 if (queue[i] == anim) {
3378                     return i;
3379                 }
3380             }
3381             return -1;
3382         };
3383
3384
3385         var correctFrame = function(tween) {
3386             var frames = tween.totalFrames;
3387             var frame = tween.currentFrame;
3388             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3389             var elapsed = (new Date() - tween.getStartTime());
3390             var tweak = 0;
3391
3392             if (elapsed < tween.duration * 1000) {
3393                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3394             } else {
3395                 tweak = frames - (frame + 1);
3396             }
3397             if (tweak > 0 && isFinite(tweak)) {
3398                 if (tween.currentFrame + tweak >= frames) {
3399                     tweak = frames - (frame + 1);
3400                 }
3401
3402                 tween.currentFrame += tweak;
3403             }
3404         };
3405     };/*
3406  * Portions of this file are based on pieces of Yahoo User Interface Library
3407  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3408  * YUI licensed under the BSD License:
3409  * http://developer.yahoo.net/yui/license.txt
3410  * <script type="text/javascript">
3411  *
3412  */
3413 Roo.lib.Bezier = new function() {
3414
3415         this.getPosition = function(points, t) {
3416             var n = points.length;
3417             var tmp = [];
3418
3419             for (var i = 0; i < n; ++i) {
3420                 tmp[i] = [points[i][0], points[i][1]];
3421             }
3422
3423             for (var j = 1; j < n; ++j) {
3424                 for (i = 0; i < n - j; ++i) {
3425                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3426                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3427                 }
3428             }
3429
3430             return [ tmp[0][0], tmp[0][1] ];
3431
3432         };
3433     };/*
3434  * Portions of this file are based on pieces of Yahoo User Interface Library
3435  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3436  * YUI licensed under the BSD License:
3437  * http://developer.yahoo.net/yui/license.txt
3438  * <script type="text/javascript">
3439  *
3440  */
3441 (function() {
3442
3443     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3444         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3445     };
3446
3447     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3448
3449     var fly = Roo.lib.AnimBase.fly;
3450     var Y = Roo.lib;
3451     var superclass = Y.ColorAnim.superclass;
3452     var proto = Y.ColorAnim.prototype;
3453
3454     proto.toString = function() {
3455         var el = this.getEl();
3456         var id = el.id || el.tagName;
3457         return ("ColorAnim " + id);
3458     };
3459
3460     proto.patterns.color = /color$/i;
3461     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3462     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3463     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3464     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3465
3466
3467     proto.parseColor = function(s) {
3468         if (s.length == 3) {
3469             return s;
3470         }
3471
3472         var c = this.patterns.hex.exec(s);
3473         if (c && c.length == 4) {
3474             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3475         }
3476
3477         c = this.patterns.rgb.exec(s);
3478         if (c && c.length == 4) {
3479             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3480         }
3481
3482         c = this.patterns.hex3.exec(s);
3483         if (c && c.length == 4) {
3484             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3485         }
3486
3487         return null;
3488     };
3489     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3490     proto.getAttribute = function(attr) {
3491         var el = this.getEl();
3492         if (this.patterns.color.test(attr)) {
3493             var val = fly(el).getStyle(attr);
3494
3495             if (this.patterns.transparent.test(val)) {
3496                 var parent = el.parentNode;
3497                 val = fly(parent).getStyle(attr);
3498
3499                 while (parent && this.patterns.transparent.test(val)) {
3500                     parent = parent.parentNode;
3501                     val = fly(parent).getStyle(attr);
3502                     if (parent.tagName.toUpperCase() == 'HTML') {
3503                         val = '#fff';
3504                     }
3505                 }
3506             }
3507         } else {
3508             val = superclass.getAttribute.call(this, attr);
3509         }
3510
3511         return val;
3512     };
3513     proto.getAttribute = function(attr) {
3514         var el = this.getEl();
3515         if (this.patterns.color.test(attr)) {
3516             var val = fly(el).getStyle(attr);
3517
3518             if (this.patterns.transparent.test(val)) {
3519                 var parent = el.parentNode;
3520                 val = fly(parent).getStyle(attr);
3521
3522                 while (parent && this.patterns.transparent.test(val)) {
3523                     parent = parent.parentNode;
3524                     val = fly(parent).getStyle(attr);
3525                     if (parent.tagName.toUpperCase() == 'HTML') {
3526                         val = '#fff';
3527                     }
3528                 }
3529             }
3530         } else {
3531             val = superclass.getAttribute.call(this, attr);
3532         }
3533
3534         return val;
3535     };
3536
3537     proto.doMethod = function(attr, start, end) {
3538         var val;
3539
3540         if (this.patterns.color.test(attr)) {
3541             val = [];
3542             for (var i = 0, len = start.length; i < len; ++i) {
3543                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3544             }
3545
3546             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3547         }
3548         else {
3549             val = superclass.doMethod.call(this, attr, start, end);
3550         }
3551
3552         return val;
3553     };
3554
3555     proto.setRuntimeAttribute = function(attr) {
3556         superclass.setRuntimeAttribute.call(this, attr);
3557
3558         if (this.patterns.color.test(attr)) {
3559             var attributes = this.attributes;
3560             var start = this.parseColor(this.runtimeAttributes[attr].start);
3561             var end = this.parseColor(this.runtimeAttributes[attr].end);
3562
3563             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3564                 end = this.parseColor(attributes[attr].by);
3565
3566                 for (var i = 0, len = start.length; i < len; ++i) {
3567                     end[i] = start[i] + end[i];
3568                 }
3569             }
3570
3571             this.runtimeAttributes[attr].start = start;
3572             this.runtimeAttributes[attr].end = end;
3573         }
3574     };
3575 })();
3576
3577 /*
3578  * Portions of this file are based on pieces of Yahoo User Interface Library
3579  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3580  * YUI licensed under the BSD License:
3581  * http://developer.yahoo.net/yui/license.txt
3582  * <script type="text/javascript">
3583  *
3584  */
3585 Roo.lib.Easing = {
3586
3587
3588     easeNone: function (t, b, c, d) {
3589         return c * t / d + b;
3590     },
3591
3592
3593     easeIn: function (t, b, c, d) {
3594         return c * (t /= d) * t + b;
3595     },
3596
3597
3598     easeOut: function (t, b, c, d) {
3599         return -c * (t /= d) * (t - 2) + b;
3600     },
3601
3602
3603     easeBoth: function (t, b, c, d) {
3604         if ((t /= d / 2) < 1) {
3605             return c / 2 * t * t + b;
3606         }
3607
3608         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3609     },
3610
3611
3612     easeInStrong: function (t, b, c, d) {
3613         return c * (t /= d) * t * t * t + b;
3614     },
3615
3616
3617     easeOutStrong: function (t, b, c, d) {
3618         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3619     },
3620
3621
3622     easeBothStrong: function (t, b, c, d) {
3623         if ((t /= d / 2) < 1) {
3624             return c / 2 * t * t * t * t + b;
3625         }
3626
3627         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3628     },
3629
3630
3631
3632     elasticIn: function (t, b, c, d, a, p) {
3633         if (t == 0) {
3634             return b;
3635         }
3636         if ((t /= d) == 1) {
3637             return b + c;
3638         }
3639         if (!p) {
3640             p = d * .3;
3641         }
3642
3643         if (!a || a < Math.abs(c)) {
3644             a = c;
3645             var s = p / 4;
3646         }
3647         else {
3648             var s = p / (2 * Math.PI) * Math.asin(c / a);
3649         }
3650
3651         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3652     },
3653
3654
3655     elasticOut: function (t, b, c, d, a, p) {
3656         if (t == 0) {
3657             return b;
3658         }
3659         if ((t /= d) == 1) {
3660             return b + c;
3661         }
3662         if (!p) {
3663             p = d * .3;
3664         }
3665
3666         if (!a || a < Math.abs(c)) {
3667             a = c;
3668             var s = p / 4;
3669         }
3670         else {
3671             var s = p / (2 * Math.PI) * Math.asin(c / a);
3672         }
3673
3674         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3675     },
3676
3677
3678     elasticBoth: function (t, b, c, d, a, p) {
3679         if (t == 0) {
3680             return b;
3681         }
3682
3683         if ((t /= d / 2) == 2) {
3684             return b + c;
3685         }
3686
3687         if (!p) {
3688             p = d * (.3 * 1.5);
3689         }
3690
3691         if (!a || a < Math.abs(c)) {
3692             a = c;
3693             var s = p / 4;
3694         }
3695         else {
3696             var s = p / (2 * Math.PI) * Math.asin(c / a);
3697         }
3698
3699         if (t < 1) {
3700             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3701                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3702         }
3703         return a * Math.pow(2, -10 * (t -= 1)) *
3704                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3705     },
3706
3707
3708
3709     backIn: function (t, b, c, d, s) {
3710         if (typeof s == 'undefined') {
3711             s = 1.70158;
3712         }
3713         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3714     },
3715
3716
3717     backOut: function (t, b, c, d, s) {
3718         if (typeof s == 'undefined') {
3719             s = 1.70158;
3720         }
3721         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3722     },
3723
3724
3725     backBoth: function (t, b, c, d, s) {
3726         if (typeof s == 'undefined') {
3727             s = 1.70158;
3728         }
3729
3730         if ((t /= d / 2 ) < 1) {
3731             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3732         }
3733         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3734     },
3735
3736
3737     bounceIn: function (t, b, c, d) {
3738         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3739     },
3740
3741
3742     bounceOut: function (t, b, c, d) {
3743         if ((t /= d) < (1 / 2.75)) {
3744             return c * (7.5625 * t * t) + b;
3745         } else if (t < (2 / 2.75)) {
3746             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3747         } else if (t < (2.5 / 2.75)) {
3748             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3749         }
3750         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3751     },
3752
3753
3754     bounceBoth: function (t, b, c, d) {
3755         if (t < d / 2) {
3756             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3757         }
3758         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3759     }
3760 };/*
3761  * Portions of this file are based on pieces of Yahoo User Interface Library
3762  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3763  * YUI licensed under the BSD License:
3764  * http://developer.yahoo.net/yui/license.txt
3765  * <script type="text/javascript">
3766  *
3767  */
3768     (function() {
3769         Roo.lib.Motion = function(el, attributes, duration, method) {
3770             if (el) {
3771                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3772             }
3773         };
3774
3775         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3776
3777
3778         var Y = Roo.lib;
3779         var superclass = Y.Motion.superclass;
3780         var proto = Y.Motion.prototype;
3781
3782         proto.toString = function() {
3783             var el = this.getEl();
3784             var id = el.id || el.tagName;
3785             return ("Motion " + id);
3786         };
3787
3788         proto.patterns.points = /^points$/i;
3789
3790         proto.setAttribute = function(attr, val, unit) {
3791             if (this.patterns.points.test(attr)) {
3792                 unit = unit || 'px';
3793                 superclass.setAttribute.call(this, 'left', val[0], unit);
3794                 superclass.setAttribute.call(this, 'top', val[1], unit);
3795             } else {
3796                 superclass.setAttribute.call(this, attr, val, unit);
3797             }
3798         };
3799
3800         proto.getAttribute = function(attr) {
3801             if (this.patterns.points.test(attr)) {
3802                 var val = [
3803                         superclass.getAttribute.call(this, 'left'),
3804                         superclass.getAttribute.call(this, 'top')
3805                         ];
3806             } else {
3807                 val = superclass.getAttribute.call(this, attr);
3808             }
3809
3810             return val;
3811         };
3812
3813         proto.doMethod = function(attr, start, end) {
3814             var val = null;
3815
3816             if (this.patterns.points.test(attr)) {
3817                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3818                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3819             } else {
3820                 val = superclass.doMethod.call(this, attr, start, end);
3821             }
3822             return val;
3823         };
3824
3825         proto.setRuntimeAttribute = function(attr) {
3826             if (this.patterns.points.test(attr)) {
3827                 var el = this.getEl();
3828                 var attributes = this.attributes;
3829                 var start;
3830                 var control = attributes['points']['control'] || [];
3831                 var end;
3832                 var i, len;
3833
3834                 if (control.length > 0 && !(control[0] instanceof Array)) {
3835                     control = [control];
3836                 } else {
3837                     var tmp = [];
3838                     for (i = 0,len = control.length; i < len; ++i) {
3839                         tmp[i] = control[i];
3840                     }
3841                     control = tmp;
3842                 }
3843
3844                 Roo.fly(el).position();
3845
3846                 if (isset(attributes['points']['from'])) {
3847                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3848                 }
3849                 else {
3850                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3851                 }
3852
3853                 start = this.getAttribute('points');
3854
3855
3856                 if (isset(attributes['points']['to'])) {
3857                     end = translateValues.call(this, attributes['points']['to'], start);
3858
3859                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3860                     for (i = 0,len = control.length; i < len; ++i) {
3861                         control[i] = translateValues.call(this, control[i], start);
3862                     }
3863
3864
3865                 } else if (isset(attributes['points']['by'])) {
3866                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3867
3868                     for (i = 0,len = control.length; i < len; ++i) {
3869                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3870                     }
3871                 }
3872
3873                 this.runtimeAttributes[attr] = [start];
3874
3875                 if (control.length > 0) {
3876                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3877                 }
3878
3879                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3880             }
3881             else {
3882                 superclass.setRuntimeAttribute.call(this, attr);
3883             }
3884         };
3885
3886         var translateValues = function(val, start) {
3887             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3888             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3889
3890             return val;
3891         };
3892
3893         var isset = function(prop) {
3894             return (typeof prop !== 'undefined');
3895         };
3896     })();
3897 /*
3898  * Portions of this file are based on pieces of Yahoo User Interface Library
3899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3900  * YUI licensed under the BSD License:
3901  * http://developer.yahoo.net/yui/license.txt
3902  * <script type="text/javascript">
3903  *
3904  */
3905     (function() {
3906         Roo.lib.Scroll = function(el, attributes, duration, method) {
3907             if (el) {
3908                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3909             }
3910         };
3911
3912         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3913
3914
3915         var Y = Roo.lib;
3916         var superclass = Y.Scroll.superclass;
3917         var proto = Y.Scroll.prototype;
3918
3919         proto.toString = function() {
3920             var el = this.getEl();
3921             var id = el.id || el.tagName;
3922             return ("Scroll " + id);
3923         };
3924
3925         proto.doMethod = function(attr, start, end) {
3926             var val = null;
3927
3928             if (attr == 'scroll') {
3929                 val = [
3930                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3931                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3932                         ];
3933
3934             } else {
3935                 val = superclass.doMethod.call(this, attr, start, end);
3936             }
3937             return val;
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             var val = null;
3942             var el = this.getEl();
3943
3944             if (attr == 'scroll') {
3945                 val = [ el.scrollLeft, el.scrollTop ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.setAttribute = function(attr, val, unit) {
3954             var el = this.getEl();
3955
3956             if (attr == 'scroll') {
3957                 el.scrollLeft = val[0];
3958                 el.scrollTop = val[1];
3959             } else {
3960                 superclass.setAttribute.call(this, attr, val, unit);
3961             }
3962         };
3963     })();
3964 /*
3965  * Based on:
3966  * Ext JS Library 1.1.1
3967  * Copyright(c) 2006-2007, Ext JS, LLC.
3968  *
3969  * Originally Released Under LGPL - original licence link has changed is not relivant.
3970  *
3971  * Fork - LGPL
3972  * <script type="text/javascript">
3973  */
3974  
3975
3976 /**
3977  * @class Roo.DomHelper
3978  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3979  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3980  * @singleton
3981  */
3982 Roo.DomHelper = function(){
3983     var tempTableEl = null;
3984     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3985     var tableRe = /^table|tbody|tr|td$/i;
3986     var xmlns = {};
3987     // build as innerHTML where available
3988     /** @ignore */
3989     var createHtml = function(o){
3990         if(typeof o == 'string'){
3991             return o;
3992         }
3993         var b = "";
3994         if(!o.tag){
3995             o.tag = "div";
3996         }
3997         b += "<" + o.tag;
3998         for(var attr in o){
3999             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4000             if(attr == "style"){
4001                 var s = o["style"];
4002                 if(typeof s == "function"){
4003                     s = s.call();
4004                 }
4005                 if(typeof s == "string"){
4006                     b += ' style="' + s + '"';
4007                 }else if(typeof s == "object"){
4008                     b += ' style="';
4009                     for(var key in s){
4010                         if(typeof s[key] != "function"){
4011                             b += key + ":" + s[key] + ";";
4012                         }
4013                     }
4014                     b += '"';
4015                 }
4016             }else{
4017                 if(attr == "cls"){
4018                     b += ' class="' + o["cls"] + '"';
4019                 }else if(attr == "htmlFor"){
4020                     b += ' for="' + o["htmlFor"] + '"';
4021                 }else{
4022                     b += " " + attr + '="' + o[attr] + '"';
4023                 }
4024             }
4025         }
4026         if(emptyTags.test(o.tag)){
4027             b += "/>";
4028         }else{
4029             b += ">";
4030             var cn = o.children || o.cn;
4031             if(cn){
4032                 //http://bugs.kde.org/show_bug.cgi?id=71506
4033                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4034                     for(var i = 0, len = cn.length; i < len; i++) {
4035                         b += createHtml(cn[i], b);
4036                     }
4037                 }else{
4038                     b += createHtml(cn, b);
4039                 }
4040             }
4041             if(o.html){
4042                 b += o.html;
4043             }
4044             b += "</" + o.tag + ">";
4045         }
4046         return b;
4047     };
4048
4049     // build as dom
4050     /** @ignore */
4051     var createDom = function(o, parentNode){
4052          
4053         // defininition craeted..
4054         var ns = false;
4055         if (o.ns && o.ns != 'html') {
4056                
4057             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4058                 xmlns[o.ns] = o.xmlns;
4059                 ns = o.xmlns;
4060             }
4061             if (typeof(xmlns[o.ns]) == 'undefined') {
4062                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4063             }
4064             ns = xmlns[o.ns];
4065         }
4066         
4067         
4068         if (typeof(o) == 'string') {
4069             return parentNode.appendChild(document.createTextNode(o));
4070         }
4071         o.tag = o.tag || div;
4072         if (o.ns && Roo.isIE) {
4073             ns = false;
4074             o.tag = o.ns + ':' + o.tag;
4075             
4076         }
4077         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4078         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4079         for(var attr in o){
4080             
4081             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4082                     attr == "style" || typeof o[attr] == "function") continue;
4083                     
4084             if(attr=="cls" && Roo.isIE){
4085                 el.className = o["cls"];
4086             }else{
4087                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4088                 else el[attr] = o[attr];
4089             }
4090         }
4091         Roo.DomHelper.applyStyles(el, o.style);
4092         var cn = o.children || o.cn;
4093         if(cn){
4094             //http://bugs.kde.org/show_bug.cgi?id=71506
4095              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4096                 for(var i = 0, len = cn.length; i < len; i++) {
4097                     createDom(cn[i], el);
4098                 }
4099             }else{
4100                 createDom(cn, el);
4101             }
4102         }
4103         if(o.html){
4104             el.innerHTML = o.html;
4105         }
4106         if(parentNode){
4107            parentNode.appendChild(el);
4108         }
4109         return el;
4110     };
4111
4112     var ieTable = function(depth, s, h, e){
4113         tempTableEl.innerHTML = [s, h, e].join('');
4114         var i = -1, el = tempTableEl;
4115         while(++i < depth){
4116             el = el.firstChild;
4117         }
4118         return el;
4119     };
4120
4121     // kill repeat to save bytes
4122     var ts = '<table>',
4123         te = '</table>',
4124         tbs = ts+'<tbody>',
4125         tbe = '</tbody>'+te,
4126         trs = tbs + '<tr>',
4127         tre = '</tr>'+tbe;
4128
4129     /**
4130      * @ignore
4131      * Nasty code for IE's broken table implementation
4132      */
4133     var insertIntoTable = function(tag, where, el, html){
4134         if(!tempTableEl){
4135             tempTableEl = document.createElement('div');
4136         }
4137         var node;
4138         var before = null;
4139         if(tag == 'td'){
4140             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4141                 return;
4142             }
4143             if(where == 'beforebegin'){
4144                 before = el;
4145                 el = el.parentNode;
4146             } else{
4147                 before = el.nextSibling;
4148                 el = el.parentNode;
4149             }
4150             node = ieTable(4, trs, html, tre);
4151         }
4152         else if(tag == 'tr'){
4153             if(where == 'beforebegin'){
4154                 before = el;
4155                 el = el.parentNode;
4156                 node = ieTable(3, tbs, html, tbe);
4157             } else if(where == 'afterend'){
4158                 before = el.nextSibling;
4159                 el = el.parentNode;
4160                 node = ieTable(3, tbs, html, tbe);
4161             } else{ // INTO a TR
4162                 if(where == 'afterbegin'){
4163                     before = el.firstChild;
4164                 }
4165                 node = ieTable(4, trs, html, tre);
4166             }
4167         } else if(tag == 'tbody'){
4168             if(where == 'beforebegin'){
4169                 before = el;
4170                 el = el.parentNode;
4171                 node = ieTable(2, ts, html, te);
4172             } else if(where == 'afterend'){
4173                 before = el.nextSibling;
4174                 el = el.parentNode;
4175                 node = ieTable(2, ts, html, te);
4176             } else{
4177                 if(where == 'afterbegin'){
4178                     before = el.firstChild;
4179                 }
4180                 node = ieTable(3, tbs, html, tbe);
4181             }
4182         } else{ // TABLE
4183             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4184                 return;
4185             }
4186             if(where == 'afterbegin'){
4187                 before = el.firstChild;
4188             }
4189             node = ieTable(2, ts, html, te);
4190         }
4191         el.insertBefore(node, before);
4192         return node;
4193     };
4194
4195     return {
4196     /** True to force the use of DOM instead of html fragments @type Boolean */
4197     useDom : false,
4198
4199     /**
4200      * Returns the markup for the passed Element(s) config
4201      * @param {Object} o The Dom object spec (and children)
4202      * @return {String}
4203      */
4204     markup : function(o){
4205         return createHtml(o);
4206     },
4207
4208     /**
4209      * Applies a style specification to an element
4210      * @param {String/HTMLElement} el The element to apply styles to
4211      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4212      * a function which returns such a specification.
4213      */
4214     applyStyles : function(el, styles){
4215         if(styles){
4216            el = Roo.fly(el);
4217            if(typeof styles == "string"){
4218                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4219                var matches;
4220                while ((matches = re.exec(styles)) != null){
4221                    el.setStyle(matches[1], matches[2]);
4222                }
4223            }else if (typeof styles == "object"){
4224                for (var style in styles){
4225                   el.setStyle(style, styles[style]);
4226                }
4227            }else if (typeof styles == "function"){
4228                 Roo.DomHelper.applyStyles(el, styles.call());
4229            }
4230         }
4231     },
4232
4233     /**
4234      * Inserts an HTML fragment into the Dom
4235      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4236      * @param {HTMLElement} el The context element
4237      * @param {String} html The HTML fragmenet
4238      * @return {HTMLElement} The new node
4239      */
4240     insertHtml : function(where, el, html){
4241         where = where.toLowerCase();
4242         if(el.insertAdjacentHTML){
4243             if(tableRe.test(el.tagName)){
4244                 var rs;
4245                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4246                     return rs;
4247                 }
4248             }
4249             switch(where){
4250                 case "beforebegin":
4251                     el.insertAdjacentHTML('BeforeBegin', html);
4252                     return el.previousSibling;
4253                 case "afterbegin":
4254                     el.insertAdjacentHTML('AfterBegin', html);
4255                     return el.firstChild;
4256                 case "beforeend":
4257                     el.insertAdjacentHTML('BeforeEnd', html);
4258                     return el.lastChild;
4259                 case "afterend":
4260                     el.insertAdjacentHTML('AfterEnd', html);
4261                     return el.nextSibling;
4262             }
4263             throw 'Illegal insertion point -> "' + where + '"';
4264         }
4265         var range = el.ownerDocument.createRange();
4266         var frag;
4267         switch(where){
4268              case "beforebegin":
4269                 range.setStartBefore(el);
4270                 frag = range.createContextualFragment(html);
4271                 el.parentNode.insertBefore(frag, el);
4272                 return el.previousSibling;
4273              case "afterbegin":
4274                 if(el.firstChild){
4275                     range.setStartBefore(el.firstChild);
4276                     frag = range.createContextualFragment(html);
4277                     el.insertBefore(frag, el.firstChild);
4278                     return el.firstChild;
4279                 }else{
4280                     el.innerHTML = html;
4281                     return el.firstChild;
4282                 }
4283             case "beforeend":
4284                 if(el.lastChild){
4285                     range.setStartAfter(el.lastChild);
4286                     frag = range.createContextualFragment(html);
4287                     el.appendChild(frag);
4288                     return el.lastChild;
4289                 }else{
4290                     el.innerHTML = html;
4291                     return el.lastChild;
4292                 }
4293             case "afterend":
4294                 range.setStartAfter(el);
4295                 frag = range.createContextualFragment(html);
4296                 el.parentNode.insertBefore(frag, el.nextSibling);
4297                 return el.nextSibling;
4298             }
4299             throw 'Illegal insertion point -> "' + where + '"';
4300     },
4301
4302     /**
4303      * Creates new Dom element(s) and inserts them before el
4304      * @param {String/HTMLElement/Element} el The context element
4305      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4306      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4307      * @return {HTMLElement/Roo.Element} The new node
4308      */
4309     insertBefore : function(el, o, returnElement){
4310         return this.doInsert(el, o, returnElement, "beforeBegin");
4311     },
4312
4313     /**
4314      * Creates new Dom element(s) and inserts them after el
4315      * @param {String/HTMLElement/Element} el The context element
4316      * @param {Object} o The Dom object spec (and children)
4317      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4318      * @return {HTMLElement/Roo.Element} The new node
4319      */
4320     insertAfter : function(el, o, returnElement){
4321         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4322     },
4323
4324     /**
4325      * Creates new Dom element(s) and inserts them as the first child of el
4326      * @param {String/HTMLElement/Element} el The context element
4327      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4328      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4329      * @return {HTMLElement/Roo.Element} The new node
4330      */
4331     insertFirst : function(el, o, returnElement){
4332         return this.doInsert(el, o, returnElement, "afterBegin");
4333     },
4334
4335     // private
4336     doInsert : function(el, o, returnElement, pos, sibling){
4337         el = Roo.getDom(el);
4338         var newNode;
4339         if(this.useDom || o.ns){
4340             newNode = createDom(o, null);
4341             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4342         }else{
4343             var html = createHtml(o);
4344             newNode = this.insertHtml(pos, el, html);
4345         }
4346         return returnElement ? Roo.get(newNode, true) : newNode;
4347     },
4348
4349     /**
4350      * Creates new Dom element(s) and appends them to el
4351      * @param {String/HTMLElement/Element} el The context element
4352      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4353      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4354      * @return {HTMLElement/Roo.Element} The new node
4355      */
4356     append : function(el, o, returnElement){
4357         el = Roo.getDom(el);
4358         var newNode;
4359         if(this.useDom || o.ns){
4360             newNode = createDom(o, null);
4361             el.appendChild(newNode);
4362         }else{
4363             var html = createHtml(o);
4364             newNode = this.insertHtml("beforeEnd", el, html);
4365         }
4366         return returnElement ? Roo.get(newNode, true) : newNode;
4367     },
4368
4369     /**
4370      * Creates new Dom element(s) and overwrites the contents of el with them
4371      * @param {String/HTMLElement/Element} el The context element
4372      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4373      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4374      * @return {HTMLElement/Roo.Element} The new node
4375      */
4376     overwrite : function(el, o, returnElement){
4377         el = Roo.getDom(el);
4378         if (o.ns) {
4379           
4380             while (el.childNodes.length) {
4381                 el.removeChild(el.firstChild);
4382             }
4383             createDom(o, el);
4384         } else {
4385             el.innerHTML = createHtml(o);   
4386         }
4387         
4388         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4389     },
4390
4391     /**
4392      * Creates a new Roo.DomHelper.Template from the Dom object spec
4393      * @param {Object} o The Dom object spec (and children)
4394      * @return {Roo.DomHelper.Template} The new template
4395      */
4396     createTemplate : function(o){
4397         var html = createHtml(o);
4398         return new Roo.Template(html);
4399     }
4400     };
4401 }();
4402 /*
4403  * Based on:
4404  * Ext JS Library 1.1.1
4405  * Copyright(c) 2006-2007, Ext JS, LLC.
4406  *
4407  * Originally Released Under LGPL - original licence link has changed is not relivant.
4408  *
4409  * Fork - LGPL
4410  * <script type="text/javascript">
4411  */
4412  
4413 /**
4414 * @class Roo.Template
4415 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4416 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4417 * Usage:
4418 <pre><code>
4419 var t = new Roo.Template({
4420     html :  '&lt;div name="{id}"&gt;' + 
4421         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4422         '&lt;/div&gt;',
4423     myformat: function (value, allValues) {
4424         return 'XX' + value;
4425     }
4426 });
4427 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4428 </code></pre>
4429 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4430 * @constructor
4431 * @param {Object} cfg - Configuration object.
4432 */
4433 Roo.Template = function(cfg){
4434     // BC!
4435     if(cfg instanceof Array){
4436         cfg = cfg.join("");
4437     }else if(arguments.length > 1){
4438         cfg = Array.prototype.join.call(arguments, "");
4439     }
4440     
4441     
4442     if (typeof(cfg) == 'object') {
4443         Roo.apply(this,cfg)
4444     } else {
4445         // bc
4446         this.html = cfg;
4447     }
4448     
4449     
4450 };
4451 Roo.Template.prototype = {
4452     
4453     /**
4454      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4455      */
4456     html : '',
4457     /**
4458      * Returns an HTML fragment of this template with the specified values applied.
4459      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4460      * @return {String} The HTML fragment
4461      */
4462     applyTemplate : function(values){
4463         try {
4464             
4465             if(this.compiled){
4466                 return this.compiled(values);
4467             }
4468             var useF = this.disableFormats !== true;
4469             var fm = Roo.util.Format, tpl = this;
4470             var fn = function(m, name, format, args){
4471                 if(format && useF){
4472                     if(format.substr(0, 5) == "this."){
4473                         return tpl.call(format.substr(5), values[name], values);
4474                     }else{
4475                         if(args){
4476                             // quoted values are required for strings in compiled templates, 
4477                             // but for non compiled we need to strip them
4478                             // quoted reversed for jsmin
4479                             var re = /^\s*['"](.*)["']\s*$/;
4480                             args = args.split(',');
4481                             for(var i = 0, len = args.length; i < len; i++){
4482                                 args[i] = args[i].replace(re, "$1");
4483                             }
4484                             args = [values[name]].concat(args);
4485                         }else{
4486                             args = [values[name]];
4487                         }
4488                         return fm[format].apply(fm, args);
4489                     }
4490                 }else{
4491                     return values[name] !== undefined ? values[name] : "";
4492                 }
4493             };
4494             return this.html.replace(this.re, fn);
4495         } catch (e) {
4496             Roo.log(e);
4497             throw e;
4498         }
4499          
4500     },
4501     
4502     /**
4503      * Sets the HTML used as the template and optionally compiles it.
4504      * @param {String} html
4505      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4506      * @return {Roo.Template} this
4507      */
4508     set : function(html, compile){
4509         this.html = html;
4510         this.compiled = null;
4511         if(compile){
4512             this.compile();
4513         }
4514         return this;
4515     },
4516     
4517     /**
4518      * True to disable format functions (defaults to false)
4519      * @type Boolean
4520      */
4521     disableFormats : false,
4522     
4523     /**
4524     * The regular expression used to match template variables 
4525     * @type RegExp
4526     * @property 
4527     */
4528     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4529     
4530     /**
4531      * Compiles the template into an internal function, eliminating the RegEx overhead.
4532      * @return {Roo.Template} this
4533      */
4534     compile : function(){
4535         var fm = Roo.util.Format;
4536         var useF = this.disableFormats !== true;
4537         var sep = Roo.isGecko ? "+" : ",";
4538         var fn = function(m, name, format, args){
4539             if(format && useF){
4540                 args = args ? ',' + args : "";
4541                 if(format.substr(0, 5) != "this."){
4542                     format = "fm." + format + '(';
4543                 }else{
4544                     format = 'this.call("'+ format.substr(5) + '", ';
4545                     args = ", values";
4546                 }
4547             }else{
4548                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4549             }
4550             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4551         };
4552         var body;
4553         // branched to use + in gecko and [].join() in others
4554         if(Roo.isGecko){
4555             body = "this.compiled = function(values){ return '" +
4556                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4557                     "';};";
4558         }else{
4559             body = ["this.compiled = function(values){ return ['"];
4560             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4561             body.push("'].join('');};");
4562             body = body.join('');
4563         }
4564         /**
4565          * eval:var:values
4566          * eval:var:fm
4567          */
4568         eval(body);
4569         return this;
4570     },
4571     
4572     // private function used to call members
4573     call : function(fnName, value, allValues){
4574         return this[fnName](value, allValues);
4575     },
4576     
4577     /**
4578      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4579      * @param {String/HTMLElement/Roo.Element} el The context element
4580      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4581      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4582      * @return {HTMLElement/Roo.Element} The new node or Element
4583      */
4584     insertFirst: function(el, values, returnElement){
4585         return this.doInsert('afterBegin', el, values, returnElement);
4586     },
4587
4588     /**
4589      * Applies the supplied values to the template and inserts the new node(s) before el.
4590      * @param {String/HTMLElement/Roo.Element} el The context element
4591      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4592      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4593      * @return {HTMLElement/Roo.Element} The new node or Element
4594      */
4595     insertBefore: function(el, values, returnElement){
4596         return this.doInsert('beforeBegin', el, values, returnElement);
4597     },
4598
4599     /**
4600      * Applies the supplied values to the template and inserts the new node(s) after el.
4601      * @param {String/HTMLElement/Roo.Element} el The context element
4602      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4603      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4604      * @return {HTMLElement/Roo.Element} The new node or Element
4605      */
4606     insertAfter : function(el, values, returnElement){
4607         return this.doInsert('afterEnd', el, values, returnElement);
4608     },
4609     
4610     /**
4611      * Applies the supplied values to the template and appends the new node(s) to el.
4612      * @param {String/HTMLElement/Roo.Element} el The context element
4613      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4614      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4615      * @return {HTMLElement/Roo.Element} The new node or Element
4616      */
4617     append : function(el, values, returnElement){
4618         return this.doInsert('beforeEnd', el, values, returnElement);
4619     },
4620
4621     doInsert : function(where, el, values, returnEl){
4622         el = Roo.getDom(el);
4623         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4624         return returnEl ? Roo.get(newNode, true) : newNode;
4625     },
4626
4627     /**
4628      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4629      * @param {String/HTMLElement/Roo.Element} el The context element
4630      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4631      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4632      * @return {HTMLElement/Roo.Element} The new node or Element
4633      */
4634     overwrite : function(el, values, returnElement){
4635         el = Roo.getDom(el);
4636         el.innerHTML = this.applyTemplate(values);
4637         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4638     }
4639 };
4640 /**
4641  * Alias for {@link #applyTemplate}
4642  * @method
4643  */
4644 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4645
4646 // backwards compat
4647 Roo.DomHelper.Template = Roo.Template;
4648
4649 /**
4650  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4651  * @param {String/HTMLElement} el A DOM element or its id
4652  * @returns {Roo.Template} The created template
4653  * @static
4654  */
4655 Roo.Template.from = function(el){
4656     el = Roo.getDom(el);
4657     return new Roo.Template(el.value || el.innerHTML);
4658 };/*
4659  * Based on:
4660  * Ext JS Library 1.1.1
4661  * Copyright(c) 2006-2007, Ext JS, LLC.
4662  *
4663  * Originally Released Under LGPL - original licence link has changed is not relivant.
4664  *
4665  * Fork - LGPL
4666  * <script type="text/javascript">
4667  */
4668  
4669
4670 /*
4671  * This is code is also distributed under MIT license for use
4672  * with jQuery and prototype JavaScript libraries.
4673  */
4674 /**
4675  * @class Roo.DomQuery
4676 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4677 <p>
4678 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4679
4680 <p>
4681 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4682 </p>
4683 <h4>Element Selectors:</h4>
4684 <ul class="list">
4685     <li> <b>*</b> any element</li>
4686     <li> <b>E</b> an element with the tag E</li>
4687     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4688     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4689     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4690     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4691 </ul>
4692 <h4>Attribute Selectors:</h4>
4693 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4694 <ul class="list">
4695     <li> <b>E[foo]</b> has an attribute "foo"</li>
4696     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4697     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4698     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4699     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4700     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4701     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4702 </ul>
4703 <h4>Pseudo Classes:</h4>
4704 <ul class="list">
4705     <li> <b>E:first-child</b> E is the first child of its parent</li>
4706     <li> <b>E:last-child</b> E is the last child of its parent</li>
4707     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4708     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4709     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4710     <li> <b>E:only-child</b> E is the only child of its parent</li>
4711     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4712     <li> <b>E:first</b> the first E in the resultset</li>
4713     <li> <b>E:last</b> the last E in the resultset</li>
4714     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4715     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4716     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4717     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4718     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4719     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4720     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4721     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4722     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4723 </ul>
4724 <h4>CSS Value Selectors:</h4>
4725 <ul class="list">
4726     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4727     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4728     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4729     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4730     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4731     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4732 </ul>
4733  * @singleton
4734  */
4735 Roo.DomQuery = function(){
4736     var cache = {}, simpleCache = {}, valueCache = {};
4737     var nonSpace = /\S/;
4738     var trimRe = /^\s+|\s+$/g;
4739     var tplRe = /\{(\d+)\}/g;
4740     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4741     var tagTokenRe = /^(#)?([\w-\*]+)/;
4742     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4743
4744     function child(p, index){
4745         var i = 0;
4746         var n = p.firstChild;
4747         while(n){
4748             if(n.nodeType == 1){
4749                if(++i == index){
4750                    return n;
4751                }
4752             }
4753             n = n.nextSibling;
4754         }
4755         return null;
4756     };
4757
4758     function next(n){
4759         while((n = n.nextSibling) && n.nodeType != 1);
4760         return n;
4761     };
4762
4763     function prev(n){
4764         while((n = n.previousSibling) && n.nodeType != 1);
4765         return n;
4766     };
4767
4768     function children(d){
4769         var n = d.firstChild, ni = -1;
4770             while(n){
4771                 var nx = n.nextSibling;
4772                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4773                     d.removeChild(n);
4774                 }else{
4775                     n.nodeIndex = ++ni;
4776                 }
4777                 n = nx;
4778             }
4779             return this;
4780         };
4781
4782     function byClassName(c, a, v){
4783         if(!v){
4784             return c;
4785         }
4786         var r = [], ri = -1, cn;
4787         for(var i = 0, ci; ci = c[i]; i++){
4788             if((' '+ci.className+' ').indexOf(v) != -1){
4789                 r[++ri] = ci;
4790             }
4791         }
4792         return r;
4793     };
4794
4795     function attrValue(n, attr){
4796         if(!n.tagName && typeof n.length != "undefined"){
4797             n = n[0];
4798         }
4799         if(!n){
4800             return null;
4801         }
4802         if(attr == "for"){
4803             return n.htmlFor;
4804         }
4805         if(attr == "class" || attr == "className"){
4806             return n.className;
4807         }
4808         return n.getAttribute(attr) || n[attr];
4809
4810     };
4811
4812     function getNodes(ns, mode, tagName){
4813         var result = [], ri = -1, cs;
4814         if(!ns){
4815             return result;
4816         }
4817         tagName = tagName || "*";
4818         if(typeof ns.getElementsByTagName != "undefined"){
4819             ns = [ns];
4820         }
4821         if(!mode){
4822             for(var i = 0, ni; ni = ns[i]; i++){
4823                 cs = ni.getElementsByTagName(tagName);
4824                 for(var j = 0, ci; ci = cs[j]; j++){
4825                     result[++ri] = ci;
4826                 }
4827             }
4828         }else if(mode == "/" || mode == ">"){
4829             var utag = tagName.toUpperCase();
4830             for(var i = 0, ni, cn; ni = ns[i]; i++){
4831                 cn = ni.children || ni.childNodes;
4832                 for(var j = 0, cj; cj = cn[j]; j++){
4833                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4834                         result[++ri] = cj;
4835                     }
4836                 }
4837             }
4838         }else if(mode == "+"){
4839             var utag = tagName.toUpperCase();
4840             for(var i = 0, n; n = ns[i]; i++){
4841                 while((n = n.nextSibling) && n.nodeType != 1);
4842                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4843                     result[++ri] = n;
4844                 }
4845             }
4846         }else if(mode == "~"){
4847             for(var i = 0, n; n = ns[i]; i++){
4848                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4849                 if(n){
4850                     result[++ri] = n;
4851                 }
4852             }
4853         }
4854         return result;
4855     };
4856
4857     function concat(a, b){
4858         if(b.slice){
4859             return a.concat(b);
4860         }
4861         for(var i = 0, l = b.length; i < l; i++){
4862             a[a.length] = b[i];
4863         }
4864         return a;
4865     }
4866
4867     function byTag(cs, tagName){
4868         if(cs.tagName || cs == document){
4869             cs = [cs];
4870         }
4871         if(!tagName){
4872             return cs;
4873         }
4874         var r = [], ri = -1;
4875         tagName = tagName.toLowerCase();
4876         for(var i = 0, ci; ci = cs[i]; i++){
4877             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4878                 r[++ri] = ci;
4879             }
4880         }
4881         return r;
4882     };
4883
4884     function byId(cs, attr, id){
4885         if(cs.tagName || cs == document){
4886             cs = [cs];
4887         }
4888         if(!id){
4889             return cs;
4890         }
4891         var r = [], ri = -1;
4892         for(var i = 0,ci; ci = cs[i]; i++){
4893             if(ci && ci.id == id){
4894                 r[++ri] = ci;
4895                 return r;
4896             }
4897         }
4898         return r;
4899     };
4900
4901     function byAttribute(cs, attr, value, op, custom){
4902         var r = [], ri = -1, st = custom=="{";
4903         var f = Roo.DomQuery.operators[op];
4904         for(var i = 0, ci; ci = cs[i]; i++){
4905             var a;
4906             if(st){
4907                 a = Roo.DomQuery.getStyle(ci, attr);
4908             }
4909             else if(attr == "class" || attr == "className"){
4910                 a = ci.className;
4911             }else if(attr == "for"){
4912                 a = ci.htmlFor;
4913             }else if(attr == "href"){
4914                 a = ci.getAttribute("href", 2);
4915             }else{
4916                 a = ci.getAttribute(attr);
4917             }
4918             if((f && f(a, value)) || (!f && a)){
4919                 r[++ri] = ci;
4920             }
4921         }
4922         return r;
4923     };
4924
4925     function byPseudo(cs, name, value){
4926         return Roo.DomQuery.pseudos[name](cs, value);
4927     };
4928
4929     // This is for IE MSXML which does not support expandos.
4930     // IE runs the same speed using setAttribute, however FF slows way down
4931     // and Safari completely fails so they need to continue to use expandos.
4932     var isIE = window.ActiveXObject ? true : false;
4933
4934     // this eval is stop the compressor from
4935     // renaming the variable to something shorter
4936     
4937     /** eval:var:batch */
4938     var batch = 30803; 
4939
4940     var key = 30803;
4941
4942     function nodupIEXml(cs){
4943         var d = ++key;
4944         cs[0].setAttribute("_nodup", d);
4945         var r = [cs[0]];
4946         for(var i = 1, len = cs.length; i < len; i++){
4947             var c = cs[i];
4948             if(!c.getAttribute("_nodup") != d){
4949                 c.setAttribute("_nodup", d);
4950                 r[r.length] = c;
4951             }
4952         }
4953         for(var i = 0, len = cs.length; i < len; i++){
4954             cs[i].removeAttribute("_nodup");
4955         }
4956         return r;
4957     }
4958
4959     function nodup(cs){
4960         if(!cs){
4961             return [];
4962         }
4963         var len = cs.length, c, i, r = cs, cj, ri = -1;
4964         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4965             return cs;
4966         }
4967         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4968             return nodupIEXml(cs);
4969         }
4970         var d = ++key;
4971         cs[0]._nodup = d;
4972         for(i = 1; c = cs[i]; i++){
4973             if(c._nodup != d){
4974                 c._nodup = d;
4975             }else{
4976                 r = [];
4977                 for(var j = 0; j < i; j++){
4978                     r[++ri] = cs[j];
4979                 }
4980                 for(j = i+1; cj = cs[j]; j++){
4981                     if(cj._nodup != d){
4982                         cj._nodup = d;
4983                         r[++ri] = cj;
4984                     }
4985                 }
4986                 return r;
4987             }
4988         }
4989         return r;
4990     }
4991
4992     function quickDiffIEXml(c1, c2){
4993         var d = ++key;
4994         for(var i = 0, len = c1.length; i < len; i++){
4995             c1[i].setAttribute("_qdiff", d);
4996         }
4997         var r = [];
4998         for(var i = 0, len = c2.length; i < len; i++){
4999             if(c2[i].getAttribute("_qdiff") != d){
5000                 r[r.length] = c2[i];
5001             }
5002         }
5003         for(var i = 0, len = c1.length; i < len; i++){
5004            c1[i].removeAttribute("_qdiff");
5005         }
5006         return r;
5007     }
5008
5009     function quickDiff(c1, c2){
5010         var len1 = c1.length;
5011         if(!len1){
5012             return c2;
5013         }
5014         if(isIE && c1[0].selectSingleNode){
5015             return quickDiffIEXml(c1, c2);
5016         }
5017         var d = ++key;
5018         for(var i = 0; i < len1; i++){
5019             c1[i]._qdiff = d;
5020         }
5021         var r = [];
5022         for(var i = 0, len = c2.length; i < len; i++){
5023             if(c2[i]._qdiff != d){
5024                 r[r.length] = c2[i];
5025             }
5026         }
5027         return r;
5028     }
5029
5030     function quickId(ns, mode, root, id){
5031         if(ns == root){
5032            var d = root.ownerDocument || root;
5033            return d.getElementById(id);
5034         }
5035         ns = getNodes(ns, mode, "*");
5036         return byId(ns, null, id);
5037     }
5038
5039     return {
5040         getStyle : function(el, name){
5041             return Roo.fly(el).getStyle(name);
5042         },
5043         /**
5044          * Compiles a selector/xpath query into a reusable function. The returned function
5045          * takes one parameter "root" (optional), which is the context node from where the query should start.
5046          * @param {String} selector The selector/xpath query
5047          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5048          * @return {Function}
5049          */
5050         compile : function(path, type){
5051             type = type || "select";
5052             
5053             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5054             var q = path, mode, lq;
5055             var tk = Roo.DomQuery.matchers;
5056             var tklen = tk.length;
5057             var mm;
5058
5059             // accept leading mode switch
5060             var lmode = q.match(modeRe);
5061             if(lmode && lmode[1]){
5062                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5063                 q = q.replace(lmode[1], "");
5064             }
5065             // strip leading slashes
5066             while(path.substr(0, 1)=="/"){
5067                 path = path.substr(1);
5068             }
5069
5070             while(q && lq != q){
5071                 lq = q;
5072                 var tm = q.match(tagTokenRe);
5073                 if(type == "select"){
5074                     if(tm){
5075                         if(tm[1] == "#"){
5076                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5077                         }else{
5078                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5079                         }
5080                         q = q.replace(tm[0], "");
5081                     }else if(q.substr(0, 1) != '@'){
5082                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5083                     }
5084                 }else{
5085                     if(tm){
5086                         if(tm[1] == "#"){
5087                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5088                         }else{
5089                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5090                         }
5091                         q = q.replace(tm[0], "");
5092                     }
5093                 }
5094                 while(!(mm = q.match(modeRe))){
5095                     var matched = false;
5096                     for(var j = 0; j < tklen; j++){
5097                         var t = tk[j];
5098                         var m = q.match(t.re);
5099                         if(m){
5100                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5101                                                     return m[i];
5102                                                 });
5103                             q = q.replace(m[0], "");
5104                             matched = true;
5105                             break;
5106                         }
5107                     }
5108                     // prevent infinite loop on bad selector
5109                     if(!matched){
5110                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5111                     }
5112                 }
5113                 if(mm[1]){
5114                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5115                     q = q.replace(mm[1], "");
5116                 }
5117             }
5118             fn[fn.length] = "return nodup(n);\n}";
5119             
5120              /** 
5121               * list of variables that need from compression as they are used by eval.
5122              *  eval:var:batch 
5123              *  eval:var:nodup
5124              *  eval:var:byTag
5125              *  eval:var:ById
5126              *  eval:var:getNodes
5127              *  eval:var:quickId
5128              *  eval:var:mode
5129              *  eval:var:root
5130              *  eval:var:n
5131              *  eval:var:byClassName
5132              *  eval:var:byPseudo
5133              *  eval:var:byAttribute
5134              *  eval:var:attrValue
5135              * 
5136              **/ 
5137             eval(fn.join(""));
5138             return f;
5139         },
5140
5141         /**
5142          * Selects a group of elements.
5143          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5144          * @param {Node} root (optional) The start of the query (defaults to document).
5145          * @return {Array}
5146          */
5147         select : function(path, root, type){
5148             if(!root || root == document){
5149                 root = document;
5150             }
5151             if(typeof root == "string"){
5152                 root = document.getElementById(root);
5153             }
5154             var paths = path.split(",");
5155             var results = [];
5156             for(var i = 0, len = paths.length; i < len; i++){
5157                 var p = paths[i].replace(trimRe, "");
5158                 if(!cache[p]){
5159                     cache[p] = Roo.DomQuery.compile(p);
5160                     if(!cache[p]){
5161                         throw p + " is not a valid selector";
5162                     }
5163                 }
5164                 var result = cache[p](root);
5165                 if(result && result != document){
5166                     results = results.concat(result);
5167                 }
5168             }
5169             if(paths.length > 1){
5170                 return nodup(results);
5171             }
5172             return results;
5173         },
5174
5175         /**
5176          * Selects a single element.
5177          * @param {String} selector The selector/xpath query
5178          * @param {Node} root (optional) The start of the query (defaults to document).
5179          * @return {Element}
5180          */
5181         selectNode : function(path, root){
5182             return Roo.DomQuery.select(path, root)[0];
5183         },
5184
5185         /**
5186          * Selects the value of a node, optionally replacing null with the defaultValue.
5187          * @param {String} selector The selector/xpath query
5188          * @param {Node} root (optional) The start of the query (defaults to document).
5189          * @param {String} defaultValue
5190          */
5191         selectValue : function(path, root, defaultValue){
5192             path = path.replace(trimRe, "");
5193             if(!valueCache[path]){
5194                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5195             }
5196             var n = valueCache[path](root);
5197             n = n[0] ? n[0] : n;
5198             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5199             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5200         },
5201
5202         /**
5203          * Selects the value of a node, parsing integers and floats.
5204          * @param {String} selector The selector/xpath query
5205          * @param {Node} root (optional) The start of the query (defaults to document).
5206          * @param {Number} defaultValue
5207          * @return {Number}
5208          */
5209         selectNumber : function(path, root, defaultValue){
5210             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5211             return parseFloat(v);
5212         },
5213
5214         /**
5215          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5216          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5217          * @param {String} selector The simple selector to test
5218          * @return {Boolean}
5219          */
5220         is : function(el, ss){
5221             if(typeof el == "string"){
5222                 el = document.getElementById(el);
5223             }
5224             var isArray = (el instanceof Array);
5225             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5226             return isArray ? (result.length == el.length) : (result.length > 0);
5227         },
5228
5229         /**
5230          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5231          * @param {Array} el An array of elements to filter
5232          * @param {String} selector The simple selector to test
5233          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5234          * the selector instead of the ones that match
5235          * @return {Array}
5236          */
5237         filter : function(els, ss, nonMatches){
5238             ss = ss.replace(trimRe, "");
5239             if(!simpleCache[ss]){
5240                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5241             }
5242             var result = simpleCache[ss](els);
5243             return nonMatches ? quickDiff(result, els) : result;
5244         },
5245
5246         /**
5247          * Collection of matching regular expressions and code snippets.
5248          */
5249         matchers : [{
5250                 re: /^\.([\w-]+)/,
5251                 select: 'n = byClassName(n, null, " {1} ");'
5252             }, {
5253                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5254                 select: 'n = byPseudo(n, "{1}", "{2}");'
5255             },{
5256                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5257                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5258             }, {
5259                 re: /^#([\w-]+)/,
5260                 select: 'n = byId(n, null, "{1}");'
5261             },{
5262                 re: /^@([\w-]+)/,
5263                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5264             }
5265         ],
5266
5267         /**
5268          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5269          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5270          */
5271         operators : {
5272             "=" : function(a, v){
5273                 return a == v;
5274             },
5275             "!=" : function(a, v){
5276                 return a != v;
5277             },
5278             "^=" : function(a, v){
5279                 return a && a.substr(0, v.length) == v;
5280             },
5281             "$=" : function(a, v){
5282                 return a && a.substr(a.length-v.length) == v;
5283             },
5284             "*=" : function(a, v){
5285                 return a && a.indexOf(v) !== -1;
5286             },
5287             "%=" : function(a, v){
5288                 return (a % v) == 0;
5289             },
5290             "|=" : function(a, v){
5291                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5292             },
5293             "~=" : function(a, v){
5294                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5295             }
5296         },
5297
5298         /**
5299          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5300          * and the argument (if any) supplied in the selector.
5301          */
5302         pseudos : {
5303             "first-child" : function(c){
5304                 var r = [], ri = -1, n;
5305                 for(var i = 0, ci; ci = n = c[i]; i++){
5306                     while((n = n.previousSibling) && n.nodeType != 1);
5307                     if(!n){
5308                         r[++ri] = ci;
5309                     }
5310                 }
5311                 return r;
5312             },
5313
5314             "last-child" : function(c){
5315                 var r = [], ri = -1, n;
5316                 for(var i = 0, ci; ci = n = c[i]; i++){
5317                     while((n = n.nextSibling) && n.nodeType != 1);
5318                     if(!n){
5319                         r[++ri] = ci;
5320                     }
5321                 }
5322                 return r;
5323             },
5324
5325             "nth-child" : function(c, a) {
5326                 var r = [], ri = -1;
5327                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5328                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5329                 for(var i = 0, n; n = c[i]; i++){
5330                     var pn = n.parentNode;
5331                     if (batch != pn._batch) {
5332                         var j = 0;
5333                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5334                             if(cn.nodeType == 1){
5335                                cn.nodeIndex = ++j;
5336                             }
5337                         }
5338                         pn._batch = batch;
5339                     }
5340                     if (f == 1) {
5341                         if (l == 0 || n.nodeIndex == l){
5342                             r[++ri] = n;
5343                         }
5344                     } else if ((n.nodeIndex + l) % f == 0){
5345                         r[++ri] = n;
5346                     }
5347                 }
5348
5349                 return r;
5350             },
5351
5352             "only-child" : function(c){
5353                 var r = [], ri = -1;;
5354                 for(var i = 0, ci; ci = c[i]; i++){
5355                     if(!prev(ci) && !next(ci)){
5356                         r[++ri] = ci;
5357                     }
5358                 }
5359                 return r;
5360             },
5361
5362             "empty" : function(c){
5363                 var r = [], ri = -1;
5364                 for(var i = 0, ci; ci = c[i]; i++){
5365                     var cns = ci.childNodes, j = 0, cn, empty = true;
5366                     while(cn = cns[j]){
5367                         ++j;
5368                         if(cn.nodeType == 1 || cn.nodeType == 3){
5369                             empty = false;
5370                             break;
5371                         }
5372                     }
5373                     if(empty){
5374                         r[++ri] = ci;
5375                     }
5376                 }
5377                 return r;
5378             },
5379
5380             "contains" : function(c, v){
5381                 var r = [], ri = -1;
5382                 for(var i = 0, ci; ci = c[i]; i++){
5383                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5384                         r[++ri] = ci;
5385                     }
5386                 }
5387                 return r;
5388             },
5389
5390             "nodeValue" : function(c, v){
5391                 var r = [], ri = -1;
5392                 for(var i = 0, ci; ci = c[i]; i++){
5393                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5394                         r[++ri] = ci;
5395                     }
5396                 }
5397                 return r;
5398             },
5399
5400             "checked" : function(c){
5401                 var r = [], ri = -1;
5402                 for(var i = 0, ci; ci = c[i]; i++){
5403                     if(ci.checked == true){
5404                         r[++ri] = ci;
5405                     }
5406                 }
5407                 return r;
5408             },
5409
5410             "not" : function(c, ss){
5411                 return Roo.DomQuery.filter(c, ss, true);
5412             },
5413
5414             "odd" : function(c){
5415                 return this["nth-child"](c, "odd");
5416             },
5417
5418             "even" : function(c){
5419                 return this["nth-child"](c, "even");
5420             },
5421
5422             "nth" : function(c, a){
5423                 return c[a-1] || [];
5424             },
5425
5426             "first" : function(c){
5427                 return c[0] || [];
5428             },
5429
5430             "last" : function(c){
5431                 return c[c.length-1] || [];
5432             },
5433
5434             "has" : function(c, ss){
5435                 var s = Roo.DomQuery.select;
5436                 var r = [], ri = -1;
5437                 for(var i = 0, ci; ci = c[i]; i++){
5438                     if(s(ss, ci).length > 0){
5439                         r[++ri] = ci;
5440                     }
5441                 }
5442                 return r;
5443             },
5444
5445             "next" : function(c, ss){
5446                 var is = Roo.DomQuery.is;
5447                 var r = [], ri = -1;
5448                 for(var i = 0, ci; ci = c[i]; i++){
5449                     var n = next(ci);
5450                     if(n && is(n, ss)){
5451                         r[++ri] = ci;
5452                     }
5453                 }
5454                 return r;
5455             },
5456
5457             "prev" : function(c, ss){
5458                 var is = Roo.DomQuery.is;
5459                 var r = [], ri = -1;
5460                 for(var i = 0, ci; ci = c[i]; i++){
5461                     var n = prev(ci);
5462                     if(n && is(n, ss)){
5463                         r[++ri] = ci;
5464                     }
5465                 }
5466                 return r;
5467             }
5468         }
5469     };
5470 }();
5471
5472 /**
5473  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5474  * @param {String} path The selector/xpath query
5475  * @param {Node} root (optional) The start of the query (defaults to document).
5476  * @return {Array}
5477  * @member Roo
5478  * @method query
5479  */
5480 Roo.query = Roo.DomQuery.select;
5481 /*
5482  * Based on:
5483  * Ext JS Library 1.1.1
5484  * Copyright(c) 2006-2007, Ext JS, LLC.
5485  *
5486  * Originally Released Under LGPL - original licence link has changed is not relivant.
5487  *
5488  * Fork - LGPL
5489  * <script type="text/javascript">
5490  */
5491
5492 /**
5493  * @class Roo.util.Observable
5494  * Base class that provides a common interface for publishing events. Subclasses are expected to
5495  * to have a property "events" with all the events defined.<br>
5496  * For example:
5497  * <pre><code>
5498  Employee = function(name){
5499     this.name = name;
5500     this.addEvents({
5501         "fired" : true,
5502         "quit" : true
5503     });
5504  }
5505  Roo.extend(Employee, Roo.util.Observable);
5506 </code></pre>
5507  * @param {Object} config properties to use (incuding events / listeners)
5508  */
5509
5510 Roo.util.Observable = function(cfg){
5511     
5512     cfg = cfg|| {};
5513     this.addEvents(cfg.events || {});
5514     if (cfg.events) {
5515         delete cfg.events; // make sure
5516     }
5517      
5518     Roo.apply(this, cfg);
5519     
5520     if(this.listeners){
5521         this.on(this.listeners);
5522         delete this.listeners;
5523     }
5524 };
5525 Roo.util.Observable.prototype = {
5526     /** 
5527  * @cfg {Object} listeners  list of events and functions to call for this object, 
5528  * For example :
5529  * <pre><code>
5530     listeners :  { 
5531        'click' : function(e) {
5532            ..... 
5533         } ,
5534         .... 
5535     } 
5536   </code></pre>
5537  */
5538     
5539     
5540     /**
5541      * Fires the specified event with the passed parameters (minus the event name).
5542      * @param {String} eventName
5543      * @param {Object...} args Variable number of parameters are passed to handlers
5544      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5545      */
5546     fireEvent : function(){
5547         var ce = this.events[arguments[0].toLowerCase()];
5548         if(typeof ce == "object"){
5549             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5550         }else{
5551             return true;
5552         }
5553     },
5554
5555     // private
5556     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5557
5558     /**
5559      * Appends an event handler to this component
5560      * @param {String}   eventName The type of event to listen for
5561      * @param {Function} handler The method the event invokes
5562      * @param {Object}   scope (optional) The scope in which to execute the handler
5563      * function. The handler function's "this" context.
5564      * @param {Object}   options (optional) An object containing handler configuration
5565      * properties. This may contain any of the following properties:<ul>
5566      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5567      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5568      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5569      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5570      * by the specified number of milliseconds. If the event fires again within that time, the original
5571      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5572      * </ul><br>
5573      * <p>
5574      * <b>Combining Options</b><br>
5575      * Using the options argument, it is possible to combine different types of listeners:<br>
5576      * <br>
5577      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5578                 <pre><code>
5579                 el.on('click', this.onClick, this, {
5580                         single: true,
5581                 delay: 100,
5582                 forumId: 4
5583                 });
5584                 </code></pre>
5585      * <p>
5586      * <b>Attaching multiple handlers in 1 call</b><br>
5587      * The method also allows for a single argument to be passed which is a config object containing properties
5588      * which specify multiple handlers.
5589      * <pre><code>
5590                 el.on({
5591                         'click': {
5592                         fn: this.onClick,
5593                         scope: this,
5594                         delay: 100
5595                 }, 
5596                 'mouseover': {
5597                         fn: this.onMouseOver,
5598                         scope: this
5599                 },
5600                 'mouseout': {
5601                         fn: this.onMouseOut,
5602                         scope: this
5603                 }
5604                 });
5605                 </code></pre>
5606      * <p>
5607      * Or a shorthand syntax which passes the same scope object to all handlers:
5608         <pre><code>
5609                 el.on({
5610                         'click': this.onClick,
5611                 'mouseover': this.onMouseOver,
5612                 'mouseout': this.onMouseOut,
5613                 scope: this
5614                 });
5615                 </code></pre>
5616      */
5617     addListener : function(eventName, fn, scope, o){
5618         if(typeof eventName == "object"){
5619             o = eventName;
5620             for(var e in o){
5621                 if(this.filterOptRe.test(e)){
5622                     continue;
5623                 }
5624                 if(typeof o[e] == "function"){
5625                     // shared options
5626                     this.addListener(e, o[e], o.scope,  o);
5627                 }else{
5628                     // individual options
5629                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5630                 }
5631             }
5632             return;
5633         }
5634         o = (!o || typeof o == "boolean") ? {} : o;
5635         eventName = eventName.toLowerCase();
5636         var ce = this.events[eventName] || true;
5637         if(typeof ce == "boolean"){
5638             ce = new Roo.util.Event(this, eventName);
5639             this.events[eventName] = ce;
5640         }
5641         ce.addListener(fn, scope, o);
5642     },
5643
5644     /**
5645      * Removes a listener
5646      * @param {String}   eventName     The type of event to listen for
5647      * @param {Function} handler        The handler to remove
5648      * @param {Object}   scope  (optional) The scope (this object) for the handler
5649      */
5650     removeListener : function(eventName, fn, scope){
5651         var ce = this.events[eventName.toLowerCase()];
5652         if(typeof ce == "object"){
5653             ce.removeListener(fn, scope);
5654         }
5655     },
5656
5657     /**
5658      * Removes all listeners for this object
5659      */
5660     purgeListeners : function(){
5661         for(var evt in this.events){
5662             if(typeof this.events[evt] == "object"){
5663                  this.events[evt].clearListeners();
5664             }
5665         }
5666     },
5667
5668     relayEvents : function(o, events){
5669         var createHandler = function(ename){
5670             return function(){
5671                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5672             };
5673         };
5674         for(var i = 0, len = events.length; i < len; i++){
5675             var ename = events[i];
5676             if(!this.events[ename]){ this.events[ename] = true; };
5677             o.on(ename, createHandler(ename), this);
5678         }
5679     },
5680
5681     /**
5682      * Used to define events on this Observable
5683      * @param {Object} object The object with the events defined
5684      */
5685     addEvents : function(o){
5686         if(!this.events){
5687             this.events = {};
5688         }
5689         Roo.applyIf(this.events, o);
5690     },
5691
5692     /**
5693      * Checks to see if this object has any listeners for a specified event
5694      * @param {String} eventName The name of the event to check for
5695      * @return {Boolean} True if the event is being listened for, else false
5696      */
5697     hasListener : function(eventName){
5698         var e = this.events[eventName];
5699         return typeof e == "object" && e.listeners.length > 0;
5700     }
5701 };
5702 /**
5703  * Appends an event handler to this element (shorthand for addListener)
5704  * @param {String}   eventName     The type of event to listen for
5705  * @param {Function} handler        The method the event invokes
5706  * @param {Object}   scope (optional) The scope in which to execute the handler
5707  * function. The handler function's "this" context.
5708  * @param {Object}   options  (optional)
5709  * @method
5710  */
5711 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5712 /**
5713  * Removes a listener (shorthand for removeListener)
5714  * @param {String}   eventName     The type of event to listen for
5715  * @param {Function} handler        The handler to remove
5716  * @param {Object}   scope  (optional) The scope (this object) for the handler
5717  * @method
5718  */
5719 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5720
5721 /**
5722  * Starts capture on the specified Observable. All events will be passed
5723  * to the supplied function with the event name + standard signature of the event
5724  * <b>before</b> the event is fired. If the supplied function returns false,
5725  * the event will not fire.
5726  * @param {Observable} o The Observable to capture
5727  * @param {Function} fn The function to call
5728  * @param {Object} scope (optional) The scope (this object) for the fn
5729  * @static
5730  */
5731 Roo.util.Observable.capture = function(o, fn, scope){
5732     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5733 };
5734
5735 /**
5736  * Removes <b>all</b> added captures from the Observable.
5737  * @param {Observable} o The Observable to release
5738  * @static
5739  */
5740 Roo.util.Observable.releaseCapture = function(o){
5741     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5742 };
5743
5744 (function(){
5745
5746     var createBuffered = function(h, o, scope){
5747         var task = new Roo.util.DelayedTask();
5748         return function(){
5749             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5750         };
5751     };
5752
5753     var createSingle = function(h, e, fn, scope){
5754         return function(){
5755             e.removeListener(fn, scope);
5756             return h.apply(scope, arguments);
5757         };
5758     };
5759
5760     var createDelayed = function(h, o, scope){
5761         return function(){
5762             var args = Array.prototype.slice.call(arguments, 0);
5763             setTimeout(function(){
5764                 h.apply(scope, args);
5765             }, o.delay || 10);
5766         };
5767     };
5768
5769     Roo.util.Event = function(obj, name){
5770         this.name = name;
5771         this.obj = obj;
5772         this.listeners = [];
5773     };
5774
5775     Roo.util.Event.prototype = {
5776         addListener : function(fn, scope, options){
5777             var o = options || {};
5778             scope = scope || this.obj;
5779             if(!this.isListening(fn, scope)){
5780                 var l = {fn: fn, scope: scope, options: o};
5781                 var h = fn;
5782                 if(o.delay){
5783                     h = createDelayed(h, o, scope);
5784                 }
5785                 if(o.single){
5786                     h = createSingle(h, this, fn, scope);
5787                 }
5788                 if(o.buffer){
5789                     h = createBuffered(h, o, scope);
5790                 }
5791                 l.fireFn = h;
5792                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5793                     this.listeners.push(l);
5794                 }else{
5795                     this.listeners = this.listeners.slice(0);
5796                     this.listeners.push(l);
5797                 }
5798             }
5799         },
5800
5801         findListener : function(fn, scope){
5802             scope = scope || this.obj;
5803             var ls = this.listeners;
5804             for(var i = 0, len = ls.length; i < len; i++){
5805                 var l = ls[i];
5806                 if(l.fn == fn && l.scope == scope){
5807                     return i;
5808                 }
5809             }
5810             return -1;
5811         },
5812
5813         isListening : function(fn, scope){
5814             return this.findListener(fn, scope) != -1;
5815         },
5816
5817         removeListener : function(fn, scope){
5818             var index;
5819             if((index = this.findListener(fn, scope)) != -1){
5820                 if(!this.firing){
5821                     this.listeners.splice(index, 1);
5822                 }else{
5823                     this.listeners = this.listeners.slice(0);
5824                     this.listeners.splice(index, 1);
5825                 }
5826                 return true;
5827             }
5828             return false;
5829         },
5830
5831         clearListeners : function(){
5832             this.listeners = [];
5833         },
5834
5835         fire : function(){
5836             var ls = this.listeners, scope, len = ls.length;
5837             if(len > 0){
5838                 this.firing = true;
5839                 var args = Array.prototype.slice.call(arguments, 0);
5840                 for(var i = 0; i < len; i++){
5841                     var l = ls[i];
5842                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5843                         this.firing = false;
5844                         return false;
5845                     }
5846                 }
5847                 this.firing = false;
5848             }
5849             return true;
5850         }
5851     };
5852 })();/*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862
5863 /**
5864  * @class Roo.EventManager
5865  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5866  * several useful events directly.
5867  * See {@link Roo.EventObject} for more details on normalized event objects.
5868  * @singleton
5869  */
5870 Roo.EventManager = function(){
5871     var docReadyEvent, docReadyProcId, docReadyState = false;
5872     var resizeEvent, resizeTask, textEvent, textSize;
5873     var E = Roo.lib.Event;
5874     var D = Roo.lib.Dom;
5875
5876
5877     var fireDocReady = function(){
5878         if(!docReadyState){
5879             docReadyState = true;
5880             Roo.isReady = true;
5881             if(docReadyProcId){
5882                 clearInterval(docReadyProcId);
5883             }
5884             if(Roo.isGecko || Roo.isOpera) {
5885                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5886             }
5887             if(Roo.isIE){
5888                 var defer = document.getElementById("ie-deferred-loader");
5889                 if(defer){
5890                     defer.onreadystatechange = null;
5891                     defer.parentNode.removeChild(defer);
5892                 }
5893             }
5894             if(docReadyEvent){
5895                 docReadyEvent.fire();
5896                 docReadyEvent.clearListeners();
5897             }
5898         }
5899     };
5900     
5901     var initDocReady = function(){
5902         docReadyEvent = new Roo.util.Event();
5903         if(Roo.isGecko || Roo.isOpera) {
5904             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5905         }else if(Roo.isIE){
5906             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5907             var defer = document.getElementById("ie-deferred-loader");
5908             defer.onreadystatechange = function(){
5909                 if(this.readyState == "complete"){
5910                     fireDocReady();
5911                 }
5912             };
5913         }else if(Roo.isSafari){ 
5914             docReadyProcId = setInterval(function(){
5915                 var rs = document.readyState;
5916                 if(rs == "complete") {
5917                     fireDocReady();     
5918                  }
5919             }, 10);
5920         }
5921         // no matter what, make sure it fires on load
5922         E.on(window, "load", fireDocReady);
5923     };
5924
5925     var createBuffered = function(h, o){
5926         var task = new Roo.util.DelayedTask(h);
5927         return function(e){
5928             // create new event object impl so new events don't wipe out properties
5929             e = new Roo.EventObjectImpl(e);
5930             task.delay(o.buffer, h, null, [e]);
5931         };
5932     };
5933
5934     var createSingle = function(h, el, ename, fn){
5935         return function(e){
5936             Roo.EventManager.removeListener(el, ename, fn);
5937             h(e);
5938         };
5939     };
5940
5941     var createDelayed = function(h, o){
5942         return function(e){
5943             // create new event object impl so new events don't wipe out properties
5944             e = new Roo.EventObjectImpl(e);
5945             setTimeout(function(){
5946                 h(e);
5947             }, o.delay || 10);
5948         };
5949     };
5950
5951     var listen = function(element, ename, opt, fn, scope){
5952         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5953         fn = fn || o.fn; scope = scope || o.scope;
5954         var el = Roo.getDom(element);
5955         if(!el){
5956             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5957         }
5958         var h = function(e){
5959             e = Roo.EventObject.setEvent(e);
5960             var t;
5961             if(o.delegate){
5962                 t = e.getTarget(o.delegate, el);
5963                 if(!t){
5964                     return;
5965                 }
5966             }else{
5967                 t = e.target;
5968             }
5969             if(o.stopEvent === true){
5970                 e.stopEvent();
5971             }
5972             if(o.preventDefault === true){
5973                e.preventDefault();
5974             }
5975             if(o.stopPropagation === true){
5976                 e.stopPropagation();
5977             }
5978
5979             if(o.normalized === false){
5980                 e = e.browserEvent;
5981             }
5982
5983             fn.call(scope || el, e, t, o);
5984         };
5985         if(o.delay){
5986             h = createDelayed(h, o);
5987         }
5988         if(o.single){
5989             h = createSingle(h, el, ename, fn);
5990         }
5991         if(o.buffer){
5992             h = createBuffered(h, o);
5993         }
5994         fn._handlers = fn._handlers || [];
5995         fn._handlers.push([Roo.id(el), ename, h]);
5996
5997         E.on(el, ename, h);
5998         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5999             el.addEventListener("DOMMouseScroll", h, false);
6000             E.on(window, 'unload', function(){
6001                 el.removeEventListener("DOMMouseScroll", h, false);
6002             });
6003         }
6004         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6005             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6006         }
6007         return h;
6008     };
6009
6010     var stopListening = function(el, ename, fn){
6011         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6012         if(hds){
6013             for(var i = 0, len = hds.length; i < len; i++){
6014                 var h = hds[i];
6015                 if(h[0] == id && h[1] == ename){
6016                     hd = h[2];
6017                     hds.splice(i, 1);
6018                     break;
6019                 }
6020             }
6021         }
6022         E.un(el, ename, hd);
6023         el = Roo.getDom(el);
6024         if(ename == "mousewheel" && el.addEventListener){
6025             el.removeEventListener("DOMMouseScroll", hd, false);
6026         }
6027         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6028             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6029         }
6030     };
6031
6032     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6033     
6034     var pub = {
6035         
6036         
6037         /** 
6038          * Fix for doc tools
6039          * @scope Roo.EventManager
6040          */
6041         
6042         
6043         /** 
6044          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6045          * object with a Roo.EventObject
6046          * @param {Function} fn        The method the event invokes
6047          * @param {Object}   scope    An object that becomes the scope of the handler
6048          * @param {boolean}  override If true, the obj passed in becomes
6049          *                             the execution scope of the listener
6050          * @return {Function} The wrapped function
6051          * @deprecated
6052          */
6053         wrap : function(fn, scope, override){
6054             return function(e){
6055                 Roo.EventObject.setEvent(e);
6056                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6057             };
6058         },
6059         
6060         /**
6061      * Appends an event handler to an element (shorthand for addListener)
6062      * @param {String/HTMLElement}   element        The html element or id to assign the
6063      * @param {String}   eventName The type of event to listen for
6064      * @param {Function} handler The method the event invokes
6065      * @param {Object}   scope (optional) The scope in which to execute the handler
6066      * function. The handler function's "this" context.
6067      * @param {Object}   options (optional) An object containing handler configuration
6068      * properties. This may contain any of the following properties:<ul>
6069      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6070      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6071      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6072      * <li>preventDefault {Boolean} True to prevent the default action</li>
6073      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6074      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6075      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6076      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6077      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6078      * by the specified number of milliseconds. If the event fires again within that time, the original
6079      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6080      * </ul><br>
6081      * <p>
6082      * <b>Combining Options</b><br>
6083      * Using the options argument, it is possible to combine different types of listeners:<br>
6084      * <br>
6085      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6086      * Code:<pre><code>
6087 el.on('click', this.onClick, this, {
6088     single: true,
6089     delay: 100,
6090     stopEvent : true,
6091     forumId: 4
6092 });</code></pre>
6093      * <p>
6094      * <b>Attaching multiple handlers in 1 call</b><br>
6095       * The method also allows for a single argument to be passed which is a config object containing properties
6096      * which specify multiple handlers.
6097      * <p>
6098      * Code:<pre><code>
6099 el.on({
6100     'click' : {
6101         fn: this.onClick
6102         scope: this,
6103         delay: 100
6104     },
6105     'mouseover' : {
6106         fn: this.onMouseOver
6107         scope: this
6108     },
6109     'mouseout' : {
6110         fn: this.onMouseOut
6111         scope: this
6112     }
6113 });</code></pre>
6114      * <p>
6115      * Or a shorthand syntax:<br>
6116      * Code:<pre><code>
6117 el.on({
6118     'click' : this.onClick,
6119     'mouseover' : this.onMouseOver,
6120     'mouseout' : this.onMouseOut
6121     scope: this
6122 });</code></pre>
6123      */
6124         addListener : function(element, eventName, fn, scope, options){
6125             if(typeof eventName == "object"){
6126                 var o = eventName;
6127                 for(var e in o){
6128                     if(propRe.test(e)){
6129                         continue;
6130                     }
6131                     if(typeof o[e] == "function"){
6132                         // shared options
6133                         listen(element, e, o, o[e], o.scope);
6134                     }else{
6135                         // individual options
6136                         listen(element, e, o[e]);
6137                     }
6138                 }
6139                 return;
6140             }
6141             return listen(element, eventName, options, fn, scope);
6142         },
6143         
6144         /**
6145          * Removes an event handler
6146          *
6147          * @param {String/HTMLElement}   element        The id or html element to remove the 
6148          *                             event from
6149          * @param {String}   eventName     The type of event
6150          * @param {Function} fn
6151          * @return {Boolean} True if a listener was actually removed
6152          */
6153         removeListener : function(element, eventName, fn){
6154             return stopListening(element, eventName, fn);
6155         },
6156         
6157         /**
6158          * Fires when the document is ready (before onload and before images are loaded). Can be 
6159          * accessed shorthanded Roo.onReady().
6160          * @param {Function} fn        The method the event invokes
6161          * @param {Object}   scope    An  object that becomes the scope of the handler
6162          * @param {boolean}  options
6163          */
6164         onDocumentReady : function(fn, scope, options){
6165             if(docReadyState){ // if it already fired
6166                 docReadyEvent.addListener(fn, scope, options);
6167                 docReadyEvent.fire();
6168                 docReadyEvent.clearListeners();
6169                 return;
6170             }
6171             if(!docReadyEvent){
6172                 initDocReady();
6173             }
6174             docReadyEvent.addListener(fn, scope, options);
6175         },
6176         
6177         /**
6178          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6179          * @param {Function} fn        The method the event invokes
6180          * @param {Object}   scope    An object that becomes the scope of the handler
6181          * @param {boolean}  options
6182          */
6183         onWindowResize : function(fn, scope, options){
6184             if(!resizeEvent){
6185                 resizeEvent = new Roo.util.Event();
6186                 resizeTask = new Roo.util.DelayedTask(function(){
6187                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6188                 });
6189                 E.on(window, "resize", function(){
6190                     if(Roo.isIE){
6191                         resizeTask.delay(50);
6192                     }else{
6193                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6194                     }
6195                 });
6196             }
6197             resizeEvent.addListener(fn, scope, options);
6198         },
6199
6200         /**
6201          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6202          * @param {Function} fn        The method the event invokes
6203          * @param {Object}   scope    An object that becomes the scope of the handler
6204          * @param {boolean}  options
6205          */
6206         onTextResize : function(fn, scope, options){
6207             if(!textEvent){
6208                 textEvent = new Roo.util.Event();
6209                 var textEl = new Roo.Element(document.createElement('div'));
6210                 textEl.dom.className = 'x-text-resize';
6211                 textEl.dom.innerHTML = 'X';
6212                 textEl.appendTo(document.body);
6213                 textSize = textEl.dom.offsetHeight;
6214                 setInterval(function(){
6215                     if(textEl.dom.offsetHeight != textSize){
6216                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6217                     }
6218                 }, this.textResizeInterval);
6219             }
6220             textEvent.addListener(fn, scope, options);
6221         },
6222
6223         /**
6224          * Removes the passed window resize listener.
6225          * @param {Function} fn        The method the event invokes
6226          * @param {Object}   scope    The scope of handler
6227          */
6228         removeResizeListener : function(fn, scope){
6229             if(resizeEvent){
6230                 resizeEvent.removeListener(fn, scope);
6231             }
6232         },
6233
6234         // private
6235         fireResize : function(){
6236             if(resizeEvent){
6237                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6238             }   
6239         },
6240         /**
6241          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6242          */
6243         ieDeferSrc : false,
6244         /**
6245          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6246          */
6247         textResizeInterval : 50
6248     };
6249     
6250     /**
6251      * Fix for doc tools
6252      * @scopeAlias pub=Roo.EventManager
6253      */
6254     
6255      /**
6256      * Appends an event handler to an element (shorthand for addListener)
6257      * @param {String/HTMLElement}   element        The html element or id to assign the
6258      * @param {String}   eventName The type of event to listen for
6259      * @param {Function} handler The method the event invokes
6260      * @param {Object}   scope (optional) The scope in which to execute the handler
6261      * function. The handler function's "this" context.
6262      * @param {Object}   options (optional) An object containing handler configuration
6263      * properties. This may contain any of the following properties:<ul>
6264      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6265      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6266      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6267      * <li>preventDefault {Boolean} True to prevent the default action</li>
6268      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6269      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6270      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6271      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6272      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6273      * by the specified number of milliseconds. If the event fires again within that time, the original
6274      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6275      * </ul><br>
6276      * <p>
6277      * <b>Combining Options</b><br>
6278      * Using the options argument, it is possible to combine different types of listeners:<br>
6279      * <br>
6280      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6281      * Code:<pre><code>
6282 el.on('click', this.onClick, this, {
6283     single: true,
6284     delay: 100,
6285     stopEvent : true,
6286     forumId: 4
6287 });</code></pre>
6288      * <p>
6289      * <b>Attaching multiple handlers in 1 call</b><br>
6290       * The method also allows for a single argument to be passed which is a config object containing properties
6291      * which specify multiple handlers.
6292      * <p>
6293      * Code:<pre><code>
6294 el.on({
6295     'click' : {
6296         fn: this.onClick
6297         scope: this,
6298         delay: 100
6299     },
6300     'mouseover' : {
6301         fn: this.onMouseOver
6302         scope: this
6303     },
6304     'mouseout' : {
6305         fn: this.onMouseOut
6306         scope: this
6307     }
6308 });</code></pre>
6309      * <p>
6310      * Or a shorthand syntax:<br>
6311      * Code:<pre><code>
6312 el.on({
6313     'click' : this.onClick,
6314     'mouseover' : this.onMouseOver,
6315     'mouseout' : this.onMouseOut
6316     scope: this
6317 });</code></pre>
6318      */
6319     pub.on = pub.addListener;
6320     pub.un = pub.removeListener;
6321
6322     pub.stoppedMouseDownEvent = new Roo.util.Event();
6323     return pub;
6324 }();
6325 /**
6326   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6327   * @param {Function} fn        The method the event invokes
6328   * @param {Object}   scope    An  object that becomes the scope of the handler
6329   * @param {boolean}  override If true, the obj passed in becomes
6330   *                             the execution scope of the listener
6331   * @member Roo
6332   * @method onReady
6333  */
6334 Roo.onReady = Roo.EventManager.onDocumentReady;
6335
6336 Roo.onReady(function(){
6337     var bd = Roo.get(document.body);
6338     if(!bd){ return; }
6339
6340     var cls = [
6341             Roo.isIE ? "roo-ie"
6342             : Roo.isGecko ? "roo-gecko"
6343             : Roo.isOpera ? "roo-opera"
6344             : Roo.isSafari ? "roo-safari" : ""];
6345
6346     if(Roo.isMac){
6347         cls.push("roo-mac");
6348     }
6349     if(Roo.isLinux){
6350         cls.push("roo-linux");
6351     }
6352     if(Roo.isBorderBox){
6353         cls.push('roo-border-box');
6354     }
6355     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6356         var p = bd.dom.parentNode;
6357         if(p){
6358             p.className += ' roo-strict';
6359         }
6360     }
6361     bd.addClass(cls.join(' '));
6362 });
6363
6364 /**
6365  * @class Roo.EventObject
6366  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6367  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6368  * Example:
6369  * <pre><code>
6370  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6371     e.preventDefault();
6372     var target = e.getTarget();
6373     ...
6374  }
6375  var myDiv = Roo.get("myDiv");
6376  myDiv.on("click", handleClick);
6377  //or
6378  Roo.EventManager.on("myDiv", 'click', handleClick);
6379  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6380  </code></pre>
6381  * @singleton
6382  */
6383 Roo.EventObject = function(){
6384     
6385     var E = Roo.lib.Event;
6386     
6387     // safari keypress events for special keys return bad keycodes
6388     var safariKeys = {
6389         63234 : 37, // left
6390         63235 : 39, // right
6391         63232 : 38, // up
6392         63233 : 40, // down
6393         63276 : 33, // page up
6394         63277 : 34, // page down
6395         63272 : 46, // delete
6396         63273 : 36, // home
6397         63275 : 35  // end
6398     };
6399
6400     // normalize button clicks
6401     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6402                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6403
6404     Roo.EventObjectImpl = function(e){
6405         if(e){
6406             this.setEvent(e.browserEvent || e);
6407         }
6408     };
6409     Roo.EventObjectImpl.prototype = {
6410         /**
6411          * Used to fix doc tools.
6412          * @scope Roo.EventObject.prototype
6413          */
6414             
6415
6416         
6417         
6418         /** The normal browser event */
6419         browserEvent : null,
6420         /** The button pressed in a mouse event */
6421         button : -1,
6422         /** True if the shift key was down during the event */
6423         shiftKey : false,
6424         /** True if the control key was down during the event */
6425         ctrlKey : false,
6426         /** True if the alt key was down during the event */
6427         altKey : false,
6428
6429         /** Key constant 
6430         * @type Number */
6431         BACKSPACE : 8,
6432         /** Key constant 
6433         * @type Number */
6434         TAB : 9,
6435         /** Key constant 
6436         * @type Number */
6437         RETURN : 13,
6438         /** Key constant 
6439         * @type Number */
6440         ENTER : 13,
6441         /** Key constant 
6442         * @type Number */
6443         SHIFT : 16,
6444         /** Key constant 
6445         * @type Number */
6446         CONTROL : 17,
6447         /** Key constant 
6448         * @type Number */
6449         ESC : 27,
6450         /** Key constant 
6451         * @type Number */
6452         SPACE : 32,
6453         /** Key constant 
6454         * @type Number */
6455         PAGEUP : 33,
6456         /** Key constant 
6457         * @type Number */
6458         PAGEDOWN : 34,
6459         /** Key constant 
6460         * @type Number */
6461         END : 35,
6462         /** Key constant 
6463         * @type Number */
6464         HOME : 36,
6465         /** Key constant 
6466         * @type Number */
6467         LEFT : 37,
6468         /** Key constant 
6469         * @type Number */
6470         UP : 38,
6471         /** Key constant 
6472         * @type Number */
6473         RIGHT : 39,
6474         /** Key constant 
6475         * @type Number */
6476         DOWN : 40,
6477         /** Key constant 
6478         * @type Number */
6479         DELETE : 46,
6480         /** Key constant 
6481         * @type Number */
6482         F5 : 116,
6483
6484            /** @private */
6485         setEvent : function(e){
6486             if(e == this || (e && e.browserEvent)){ // already wrapped
6487                 return e;
6488             }
6489             this.browserEvent = e;
6490             if(e){
6491                 // normalize buttons
6492                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6493                 if(e.type == 'click' && this.button == -1){
6494                     this.button = 0;
6495                 }
6496                 this.type = e.type;
6497                 this.shiftKey = e.shiftKey;
6498                 // mac metaKey behaves like ctrlKey
6499                 this.ctrlKey = e.ctrlKey || e.metaKey;
6500                 this.altKey = e.altKey;
6501                 // in getKey these will be normalized for the mac
6502                 this.keyCode = e.keyCode;
6503                 // keyup warnings on firefox.
6504                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6505                 // cache the target for the delayed and or buffered events
6506                 this.target = E.getTarget(e);
6507                 // same for XY
6508                 this.xy = E.getXY(e);
6509             }else{
6510                 this.button = -1;
6511                 this.shiftKey = false;
6512                 this.ctrlKey = false;
6513                 this.altKey = false;
6514                 this.keyCode = 0;
6515                 this.charCode =0;
6516                 this.target = null;
6517                 this.xy = [0, 0];
6518             }
6519             return this;
6520         },
6521
6522         /**
6523          * Stop the event (preventDefault and stopPropagation)
6524          */
6525         stopEvent : function(){
6526             if(this.browserEvent){
6527                 if(this.browserEvent.type == 'mousedown'){
6528                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6529                 }
6530                 E.stopEvent(this.browserEvent);
6531             }
6532         },
6533
6534         /**
6535          * Prevents the browsers default handling of the event.
6536          */
6537         preventDefault : function(){
6538             if(this.browserEvent){
6539                 E.preventDefault(this.browserEvent);
6540             }
6541         },
6542
6543         /** @private */
6544         isNavKeyPress : function(){
6545             var k = this.keyCode;
6546             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6547             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6548         },
6549
6550         isSpecialKey : function(){
6551             var k = this.keyCode;
6552             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6553             (k == 16) || (k == 17) ||
6554             (k >= 18 && k <= 20) ||
6555             (k >= 33 && k <= 35) ||
6556             (k >= 36 && k <= 39) ||
6557             (k >= 44 && k <= 45);
6558         },
6559         /**
6560          * Cancels bubbling of the event.
6561          */
6562         stopPropagation : function(){
6563             if(this.browserEvent){
6564                 if(this.type == 'mousedown'){
6565                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6566                 }
6567                 E.stopPropagation(this.browserEvent);
6568             }
6569         },
6570
6571         /**
6572          * Gets the key code for the event.
6573          * @return {Number}
6574          */
6575         getCharCode : function(){
6576             return this.charCode || this.keyCode;
6577         },
6578
6579         /**
6580          * Returns a normalized keyCode for the event.
6581          * @return {Number} The key code
6582          */
6583         getKey : function(){
6584             var k = this.keyCode || this.charCode;
6585             return Roo.isSafari ? (safariKeys[k] || k) : k;
6586         },
6587
6588         /**
6589          * Gets the x coordinate of the event.
6590          * @return {Number}
6591          */
6592         getPageX : function(){
6593             return this.xy[0];
6594         },
6595
6596         /**
6597          * Gets the y coordinate of the event.
6598          * @return {Number}
6599          */
6600         getPageY : function(){
6601             return this.xy[1];
6602         },
6603
6604         /**
6605          * Gets the time of the event.
6606          * @return {Number}
6607          */
6608         getTime : function(){
6609             if(this.browserEvent){
6610                 return E.getTime(this.browserEvent);
6611             }
6612             return null;
6613         },
6614
6615         /**
6616          * Gets the page coordinates of the event.
6617          * @return {Array} The xy values like [x, y]
6618          */
6619         getXY : function(){
6620             return this.xy;
6621         },
6622
6623         /**
6624          * Gets the target for the event.
6625          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6626          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6627                 search as a number or element (defaults to 10 || document.body)
6628          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6629          * @return {HTMLelement}
6630          */
6631         getTarget : function(selector, maxDepth, returnEl){
6632             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6633         },
6634         /**
6635          * Gets the related target.
6636          * @return {HTMLElement}
6637          */
6638         getRelatedTarget : function(){
6639             if(this.browserEvent){
6640                 return E.getRelatedTarget(this.browserEvent);
6641             }
6642             return null;
6643         },
6644
6645         /**
6646          * Normalizes mouse wheel delta across browsers
6647          * @return {Number} The delta
6648          */
6649         getWheelDelta : function(){
6650             var e = this.browserEvent;
6651             var delta = 0;
6652             if(e.wheelDelta){ /* IE/Opera. */
6653                 delta = e.wheelDelta/120;
6654             }else if(e.detail){ /* Mozilla case. */
6655                 delta = -e.detail/3;
6656             }
6657             return delta;
6658         },
6659
6660         /**
6661          * Returns true if the control, meta, shift or alt key was pressed during this event.
6662          * @return {Boolean}
6663          */
6664         hasModifier : function(){
6665             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6666         },
6667
6668         /**
6669          * Returns true if the target of this event equals el or is a child of el
6670          * @param {String/HTMLElement/Element} el
6671          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6672          * @return {Boolean}
6673          */
6674         within : function(el, related){
6675             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6676             return t && Roo.fly(el).contains(t);
6677         },
6678
6679         getPoint : function(){
6680             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6681         }
6682     };
6683
6684     return new Roo.EventObjectImpl();
6685 }();
6686             
6687     /*
6688  * Based on:
6689  * Ext JS Library 1.1.1
6690  * Copyright(c) 2006-2007, Ext JS, LLC.
6691  *
6692  * Originally Released Under LGPL - original licence link has changed is not relivant.
6693  *
6694  * Fork - LGPL
6695  * <script type="text/javascript">
6696  */
6697
6698  
6699 // was in Composite Element!??!?!
6700  
6701 (function(){
6702     var D = Roo.lib.Dom;
6703     var E = Roo.lib.Event;
6704     var A = Roo.lib.Anim;
6705
6706     // local style camelizing for speed
6707     var propCache = {};
6708     var camelRe = /(-[a-z])/gi;
6709     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6710     var view = document.defaultView;
6711
6712 /**
6713  * @class Roo.Element
6714  * Represents an Element in the DOM.<br><br>
6715  * Usage:<br>
6716 <pre><code>
6717 var el = Roo.get("my-div");
6718
6719 // or with getEl
6720 var el = getEl("my-div");
6721
6722 // or with a DOM element
6723 var el = Roo.get(myDivElement);
6724 </code></pre>
6725  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6726  * each call instead of constructing a new one.<br><br>
6727  * <b>Animations</b><br />
6728  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6729  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6730 <pre>
6731 Option    Default   Description
6732 --------- --------  ---------------------------------------------
6733 duration  .35       The duration of the animation in seconds
6734 easing    easeOut   The YUI easing method
6735 callback  none      A function to execute when the anim completes
6736 scope     this      The scope (this) of the callback function
6737 </pre>
6738 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6739 * manipulate the animation. Here's an example:
6740 <pre><code>
6741 var el = Roo.get("my-div");
6742
6743 // no animation
6744 el.setWidth(100);
6745
6746 // default animation
6747 el.setWidth(100, true);
6748
6749 // animation with some options set
6750 el.setWidth(100, {
6751     duration: 1,
6752     callback: this.foo,
6753     scope: this
6754 });
6755
6756 // using the "anim" property to get the Anim object
6757 var opt = {
6758     duration: 1,
6759     callback: this.foo,
6760     scope: this
6761 };
6762 el.setWidth(100, opt);
6763 ...
6764 if(opt.anim.isAnimated()){
6765     opt.anim.stop();
6766 }
6767 </code></pre>
6768 * <b> Composite (Collections of) Elements</b><br />
6769  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6770  * @constructor Create a new Element directly.
6771  * @param {String/HTMLElement} element
6772  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6773  */
6774     Roo.Element = function(element, forceNew){
6775         var dom = typeof element == "string" ?
6776                 document.getElementById(element) : element;
6777         if(!dom){ // invalid id/element
6778             return null;
6779         }
6780         var id = dom.id;
6781         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6782             return Roo.Element.cache[id];
6783         }
6784
6785         /**
6786          * The DOM element
6787          * @type HTMLElement
6788          */
6789         this.dom = dom;
6790
6791         /**
6792          * The DOM element ID
6793          * @type String
6794          */
6795         this.id = id || Roo.id(dom);
6796     };
6797
6798     var El = Roo.Element;
6799
6800     El.prototype = {
6801         /**
6802          * The element's default display mode  (defaults to "")
6803          * @type String
6804          */
6805         originalDisplay : "",
6806
6807         visibilityMode : 1,
6808         /**
6809          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6810          * @type String
6811          */
6812         defaultUnit : "px",
6813         /**
6814          * Sets the element's visibility mode. When setVisible() is called it
6815          * will use this to determine whether to set the visibility or the display property.
6816          * @param visMode Element.VISIBILITY or Element.DISPLAY
6817          * @return {Roo.Element} this
6818          */
6819         setVisibilityMode : function(visMode){
6820             this.visibilityMode = visMode;
6821             return this;
6822         },
6823         /**
6824          * Convenience method for setVisibilityMode(Element.DISPLAY)
6825          * @param {String} display (optional) What to set display to when visible
6826          * @return {Roo.Element} this
6827          */
6828         enableDisplayMode : function(display){
6829             this.setVisibilityMode(El.DISPLAY);
6830             if(typeof display != "undefined") this.originalDisplay = display;
6831             return this;
6832         },
6833
6834         /**
6835          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6836          * @param {String} selector The simple selector to test
6837          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6838                 search as a number or element (defaults to 10 || document.body)
6839          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6840          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6841          */
6842         findParent : function(simpleSelector, maxDepth, returnEl){
6843             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6844             maxDepth = maxDepth || 50;
6845             if(typeof maxDepth != "number"){
6846                 stopEl = Roo.getDom(maxDepth);
6847                 maxDepth = 10;
6848             }
6849             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6850                 if(dq.is(p, simpleSelector)){
6851                     return returnEl ? Roo.get(p) : p;
6852                 }
6853                 depth++;
6854                 p = p.parentNode;
6855             }
6856             return null;
6857         },
6858
6859
6860         /**
6861          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6862          * @param {String} selector The simple selector to test
6863          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6864                 search as a number or element (defaults to 10 || document.body)
6865          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6866          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6867          */
6868         findParentNode : function(simpleSelector, maxDepth, returnEl){
6869             var p = Roo.fly(this.dom.parentNode, '_internal');
6870             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6871         },
6872
6873         /**
6874          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6875          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6876          * @param {String} selector The simple selector to test
6877          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6878                 search as a number or element (defaults to 10 || document.body)
6879          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6880          */
6881         up : function(simpleSelector, maxDepth){
6882             return this.findParentNode(simpleSelector, maxDepth, true);
6883         },
6884
6885
6886
6887         /**
6888          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6889          * @param {String} selector The simple selector to test
6890          * @return {Boolean} True if this element matches the selector, else false
6891          */
6892         is : function(simpleSelector){
6893             return Roo.DomQuery.is(this.dom, simpleSelector);
6894         },
6895
6896         /**
6897          * Perform animation on this element.
6898          * @param {Object} args The YUI animation control args
6899          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6900          * @param {Function} onComplete (optional) Function to call when animation completes
6901          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6902          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6903          * @return {Roo.Element} this
6904          */
6905         animate : function(args, duration, onComplete, easing, animType){
6906             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6907             return this;
6908         },
6909
6910         /*
6911          * @private Internal animation call
6912          */
6913         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6914             animType = animType || 'run';
6915             opt = opt || {};
6916             var anim = Roo.lib.Anim[animType](
6917                 this.dom, args,
6918                 (opt.duration || defaultDur) || .35,
6919                 (opt.easing || defaultEase) || 'easeOut',
6920                 function(){
6921                     Roo.callback(cb, this);
6922                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6923                 },
6924                 this
6925             );
6926             opt.anim = anim;
6927             return anim;
6928         },
6929
6930         // private legacy anim prep
6931         preanim : function(a, i){
6932             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6933         },
6934
6935         /**
6936          * Removes worthless text nodes
6937          * @param {Boolean} forceReclean (optional) By default the element
6938          * keeps track if it has been cleaned already so
6939          * you can call this over and over. However, if you update the element and
6940          * need to force a reclean, you can pass true.
6941          */
6942         clean : function(forceReclean){
6943             if(this.isCleaned && forceReclean !== true){
6944                 return this;
6945             }
6946             var ns = /\S/;
6947             var d = this.dom, n = d.firstChild, ni = -1;
6948             while(n){
6949                 var nx = n.nextSibling;
6950                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6951                     d.removeChild(n);
6952                 }else{
6953                     n.nodeIndex = ++ni;
6954                 }
6955                 n = nx;
6956             }
6957             this.isCleaned = true;
6958             return this;
6959         },
6960
6961         // private
6962         calcOffsetsTo : function(el){
6963             el = Roo.get(el);
6964             var d = el.dom;
6965             var restorePos = false;
6966             if(el.getStyle('position') == 'static'){
6967                 el.position('relative');
6968                 restorePos = true;
6969             }
6970             var x = 0, y =0;
6971             var op = this.dom;
6972             while(op && op != d && op.tagName != 'HTML'){
6973                 x+= op.offsetLeft;
6974                 y+= op.offsetTop;
6975                 op = op.offsetParent;
6976             }
6977             if(restorePos){
6978                 el.position('static');
6979             }
6980             return [x, y];
6981         },
6982
6983         /**
6984          * Scrolls this element into view within the passed container.
6985          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6986          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6987          * @return {Roo.Element} this
6988          */
6989         scrollIntoView : function(container, hscroll){
6990             var c = Roo.getDom(container) || document.body;
6991             var el = this.dom;
6992
6993             var o = this.calcOffsetsTo(c),
6994                 l = o[0],
6995                 t = o[1],
6996                 b = t+el.offsetHeight,
6997                 r = l+el.offsetWidth;
6998
6999             var ch = c.clientHeight;
7000             var ct = parseInt(c.scrollTop, 10);
7001             var cl = parseInt(c.scrollLeft, 10);
7002             var cb = ct + ch;
7003             var cr = cl + c.clientWidth;
7004
7005             if(t < ct){
7006                 c.scrollTop = t;
7007             }else if(b > cb){
7008                 c.scrollTop = b-ch;
7009             }
7010
7011             if(hscroll !== false){
7012                 if(l < cl){
7013                     c.scrollLeft = l;
7014                 }else if(r > cr){
7015                     c.scrollLeft = r-c.clientWidth;
7016                 }
7017             }
7018             return this;
7019         },
7020
7021         // private
7022         scrollChildIntoView : function(child, hscroll){
7023             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7024         },
7025
7026         /**
7027          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7028          * the new height may not be available immediately.
7029          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7030          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7031          * @param {Function} onComplete (optional) Function to call when animation completes
7032          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7033          * @return {Roo.Element} this
7034          */
7035         autoHeight : function(animate, duration, onComplete, easing){
7036             var oldHeight = this.getHeight();
7037             this.clip();
7038             this.setHeight(1); // force clipping
7039             setTimeout(function(){
7040                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7041                 if(!animate){
7042                     this.setHeight(height);
7043                     this.unclip();
7044                     if(typeof onComplete == "function"){
7045                         onComplete();
7046                     }
7047                 }else{
7048                     this.setHeight(oldHeight); // restore original height
7049                     this.setHeight(height, animate, duration, function(){
7050                         this.unclip();
7051                         if(typeof onComplete == "function") onComplete();
7052                     }.createDelegate(this), easing);
7053                 }
7054             }.createDelegate(this), 0);
7055             return this;
7056         },
7057
7058         /**
7059          * Returns true if this element is an ancestor of the passed element
7060          * @param {HTMLElement/String} el The element to check
7061          * @return {Boolean} True if this element is an ancestor of el, else false
7062          */
7063         contains : function(el){
7064             if(!el){return false;}
7065             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7066         },
7067
7068         /**
7069          * Checks whether the element is currently visible using both visibility and display properties.
7070          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7071          * @return {Boolean} True if the element is currently visible, else false
7072          */
7073         isVisible : function(deep) {
7074             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7075             if(deep !== true || !vis){
7076                 return vis;
7077             }
7078             var p = this.dom.parentNode;
7079             while(p && p.tagName.toLowerCase() != "body"){
7080                 if(!Roo.fly(p, '_isVisible').isVisible()){
7081                     return false;
7082                 }
7083                 p = p.parentNode;
7084             }
7085             return true;
7086         },
7087
7088         /**
7089          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7090          * @param {String} selector The CSS selector
7091          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7092          * @return {CompositeElement/CompositeElementLite} The composite element
7093          */
7094         select : function(selector, unique){
7095             return El.select(selector, unique, this.dom);
7096         },
7097
7098         /**
7099          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7100          * @param {String} selector The CSS selector
7101          * @return {Array} An array of the matched nodes
7102          */
7103         query : function(selector, unique){
7104             return Roo.DomQuery.select(selector, this.dom);
7105         },
7106
7107         /**
7108          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7109          * @param {String} selector The CSS selector
7110          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7111          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7112          */
7113         child : function(selector, returnDom){
7114             var n = Roo.DomQuery.selectNode(selector, this.dom);
7115             return returnDom ? n : Roo.get(n);
7116         },
7117
7118         /**
7119          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7120          * @param {String} selector The CSS selector
7121          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7122          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7123          */
7124         down : function(selector, returnDom){
7125             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7126             return returnDom ? n : Roo.get(n);
7127         },
7128
7129         /**
7130          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7131          * @param {String} group The group the DD object is member of
7132          * @param {Object} config The DD config object
7133          * @param {Object} overrides An object containing methods to override/implement on the DD object
7134          * @return {Roo.dd.DD} The DD object
7135          */
7136         initDD : function(group, config, overrides){
7137             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7138             return Roo.apply(dd, overrides);
7139         },
7140
7141         /**
7142          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7143          * @param {String} group The group the DDProxy object is member of
7144          * @param {Object} config The DDProxy config object
7145          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7146          * @return {Roo.dd.DDProxy} The DDProxy object
7147          */
7148         initDDProxy : function(group, config, overrides){
7149             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7150             return Roo.apply(dd, overrides);
7151         },
7152
7153         /**
7154          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7155          * @param {String} group The group the DDTarget object is member of
7156          * @param {Object} config The DDTarget config object
7157          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7158          * @return {Roo.dd.DDTarget} The DDTarget object
7159          */
7160         initDDTarget : function(group, config, overrides){
7161             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7162             return Roo.apply(dd, overrides);
7163         },
7164
7165         /**
7166          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7167          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7168          * @param {Boolean} visible Whether the element is visible
7169          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7170          * @return {Roo.Element} this
7171          */
7172          setVisible : function(visible, animate){
7173             if(!animate || !A){
7174                 if(this.visibilityMode == El.DISPLAY){
7175                     this.setDisplayed(visible);
7176                 }else{
7177                     this.fixDisplay();
7178                     this.dom.style.visibility = visible ? "visible" : "hidden";
7179                 }
7180             }else{
7181                 // closure for composites
7182                 var dom = this.dom;
7183                 var visMode = this.visibilityMode;
7184                 if(visible){
7185                     this.setOpacity(.01);
7186                     this.setVisible(true);
7187                 }
7188                 this.anim({opacity: { to: (visible?1:0) }},
7189                       this.preanim(arguments, 1),
7190                       null, .35, 'easeIn', function(){
7191                          if(!visible){
7192                              if(visMode == El.DISPLAY){
7193                                  dom.style.display = "none";
7194                              }else{
7195                                  dom.style.visibility = "hidden";
7196                              }
7197                              Roo.get(dom).setOpacity(1);
7198                          }
7199                      });
7200             }
7201             return this;
7202         },
7203
7204         /**
7205          * Returns true if display is not "none"
7206          * @return {Boolean}
7207          */
7208         isDisplayed : function() {
7209             return this.getStyle("display") != "none";
7210         },
7211
7212         /**
7213          * Toggles the element's visibility or display, depending on visibility mode.
7214          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7215          * @return {Roo.Element} this
7216          */
7217         toggle : function(animate){
7218             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7219             return this;
7220         },
7221
7222         /**
7223          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7224          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7225          * @return {Roo.Element} this
7226          */
7227         setDisplayed : function(value) {
7228             if(typeof value == "boolean"){
7229                value = value ? this.originalDisplay : "none";
7230             }
7231             this.setStyle("display", value);
7232             return this;
7233         },
7234
7235         /**
7236          * Tries to focus the element. Any exceptions are caught and ignored.
7237          * @return {Roo.Element} this
7238          */
7239         focus : function() {
7240             try{
7241                 this.dom.focus();
7242             }catch(e){}
7243             return this;
7244         },
7245
7246         /**
7247          * Tries to blur the element. Any exceptions are caught and ignored.
7248          * @return {Roo.Element} this
7249          */
7250         blur : function() {
7251             try{
7252                 this.dom.blur();
7253             }catch(e){}
7254             return this;
7255         },
7256
7257         /**
7258          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7259          * @param {String/Array} className The CSS class to add, or an array of classes
7260          * @return {Roo.Element} this
7261          */
7262         addClass : function(className){
7263             if(className instanceof Array){
7264                 for(var i = 0, len = className.length; i < len; i++) {
7265                     this.addClass(className[i]);
7266                 }
7267             }else{
7268                 if(className && !this.hasClass(className)){
7269                     this.dom.className = this.dom.className + " " + className;
7270                 }
7271             }
7272             return this;
7273         },
7274
7275         /**
7276          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7277          * @param {String/Array} className The CSS class to add, or an array of classes
7278          * @return {Roo.Element} this
7279          */
7280         radioClass : function(className){
7281             var siblings = this.dom.parentNode.childNodes;
7282             for(var i = 0; i < siblings.length; i++) {
7283                 var s = siblings[i];
7284                 if(s.nodeType == 1){
7285                     Roo.get(s).removeClass(className);
7286                 }
7287             }
7288             this.addClass(className);
7289             return this;
7290         },
7291
7292         /**
7293          * Removes one or more CSS classes from the element.
7294          * @param {String/Array} className The CSS class to remove, or an array of classes
7295          * @return {Roo.Element} this
7296          */
7297         removeClass : function(className){
7298             if(!className || !this.dom.className){
7299                 return this;
7300             }
7301             if(className instanceof Array){
7302                 for(var i = 0, len = className.length; i < len; i++) {
7303                     this.removeClass(className[i]);
7304                 }
7305             }else{
7306                 if(this.hasClass(className)){
7307                     var re = this.classReCache[className];
7308                     if (!re) {
7309                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7310                        this.classReCache[className] = re;
7311                     }
7312                     this.dom.className =
7313                         this.dom.className.replace(re, " ");
7314                 }
7315             }
7316             return this;
7317         },
7318
7319         // private
7320         classReCache: {},
7321
7322         /**
7323          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7324          * @param {String} className The CSS class to toggle
7325          * @return {Roo.Element} this
7326          */
7327         toggleClass : function(className){
7328             if(this.hasClass(className)){
7329                 this.removeClass(className);
7330             }else{
7331                 this.addClass(className);
7332             }
7333             return this;
7334         },
7335
7336         /**
7337          * Checks if the specified CSS class exists on this element's DOM node.
7338          * @param {String} className The CSS class to check for
7339          * @return {Boolean} True if the class exists, else false
7340          */
7341         hasClass : function(className){
7342             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7343         },
7344
7345         /**
7346          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7347          * @param {String} oldClassName The CSS class to replace
7348          * @param {String} newClassName The replacement CSS class
7349          * @return {Roo.Element} this
7350          */
7351         replaceClass : function(oldClassName, newClassName){
7352             this.removeClass(oldClassName);
7353             this.addClass(newClassName);
7354             return this;
7355         },
7356
7357         /**
7358          * Returns an object with properties matching the styles requested.
7359          * For example, el.getStyles('color', 'font-size', 'width') might return
7360          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7361          * @param {String} style1 A style name
7362          * @param {String} style2 A style name
7363          * @param {String} etc.
7364          * @return {Object} The style object
7365          */
7366         getStyles : function(){
7367             var a = arguments, len = a.length, r = {};
7368             for(var i = 0; i < len; i++){
7369                 r[a[i]] = this.getStyle(a[i]);
7370             }
7371             return r;
7372         },
7373
7374         /**
7375          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7376          * @param {String} property The style property whose value is returned.
7377          * @return {String} The current value of the style property for this element.
7378          */
7379         getStyle : function(){
7380             return view && view.getComputedStyle ?
7381                 function(prop){
7382                     var el = this.dom, v, cs, camel;
7383                     if(prop == 'float'){
7384                         prop = "cssFloat";
7385                     }
7386                     if(el.style && (v = el.style[prop])){
7387                         return v;
7388                     }
7389                     if(cs = view.getComputedStyle(el, "")){
7390                         if(!(camel = propCache[prop])){
7391                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7392                         }
7393                         return cs[camel];
7394                     }
7395                     return null;
7396                 } :
7397                 function(prop){
7398                     var el = this.dom, v, cs, camel;
7399                     if(prop == 'opacity'){
7400                         if(typeof el.style.filter == 'string'){
7401                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7402                             if(m){
7403                                 var fv = parseFloat(m[1]);
7404                                 if(!isNaN(fv)){
7405                                     return fv ? fv / 100 : 0;
7406                                 }
7407                             }
7408                         }
7409                         return 1;
7410                     }else if(prop == 'float'){
7411                         prop = "styleFloat";
7412                     }
7413                     if(!(camel = propCache[prop])){
7414                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7415                     }
7416                     if(v = el.style[camel]){
7417                         return v;
7418                     }
7419                     if(cs = el.currentStyle){
7420                         return cs[camel];
7421                     }
7422                     return null;
7423                 };
7424         }(),
7425
7426         /**
7427          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7428          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7429          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7430          * @return {Roo.Element} this
7431          */
7432         setStyle : function(prop, value){
7433             if(typeof prop == "string"){
7434                 
7435                 if (prop == 'float') {
7436                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7437                     return this;
7438                 }
7439                 
7440                 var camel;
7441                 if(!(camel = propCache[prop])){
7442                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7443                 }
7444                 
7445                 if(camel == 'opacity') {
7446                     this.setOpacity(value);
7447                 }else{
7448                     this.dom.style[camel] = value;
7449                 }
7450             }else{
7451                 for(var style in prop){
7452                     if(typeof prop[style] != "function"){
7453                        this.setStyle(style, prop[style]);
7454                     }
7455                 }
7456             }
7457             return this;
7458         },
7459
7460         /**
7461          * More flexible version of {@link #setStyle} for setting style properties.
7462          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7463          * a function which returns such a specification.
7464          * @return {Roo.Element} this
7465          */
7466         applyStyles : function(style){
7467             Roo.DomHelper.applyStyles(this.dom, style);
7468             return this;
7469         },
7470
7471         /**
7472           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7473           * @return {Number} The X position of the element
7474           */
7475         getX : function(){
7476             return D.getX(this.dom);
7477         },
7478
7479         /**
7480           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7481           * @return {Number} The Y position of the element
7482           */
7483         getY : function(){
7484             return D.getY(this.dom);
7485         },
7486
7487         /**
7488           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7489           * @return {Array} The XY position of the element
7490           */
7491         getXY : function(){
7492             return D.getXY(this.dom);
7493         },
7494
7495         /**
7496          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7497          * @param {Number} The X position of the element
7498          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7499          * @return {Roo.Element} this
7500          */
7501         setX : function(x, animate){
7502             if(!animate || !A){
7503                 D.setX(this.dom, x);
7504             }else{
7505                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7506             }
7507             return this;
7508         },
7509
7510         /**
7511          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7512          * @param {Number} The Y position of the element
7513          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7514          * @return {Roo.Element} this
7515          */
7516         setY : function(y, animate){
7517             if(!animate || !A){
7518                 D.setY(this.dom, y);
7519             }else{
7520                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7521             }
7522             return this;
7523         },
7524
7525         /**
7526          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7527          * @param {String} left The left CSS property value
7528          * @return {Roo.Element} this
7529          */
7530         setLeft : function(left){
7531             this.setStyle("left", this.addUnits(left));
7532             return this;
7533         },
7534
7535         /**
7536          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7537          * @param {String} top The top CSS property value
7538          * @return {Roo.Element} this
7539          */
7540         setTop : function(top){
7541             this.setStyle("top", this.addUnits(top));
7542             return this;
7543         },
7544
7545         /**
7546          * Sets the element's CSS right style.
7547          * @param {String} right The right CSS property value
7548          * @return {Roo.Element} this
7549          */
7550         setRight : function(right){
7551             this.setStyle("right", this.addUnits(right));
7552             return this;
7553         },
7554
7555         /**
7556          * Sets the element's CSS bottom style.
7557          * @param {String} bottom The bottom CSS property value
7558          * @return {Roo.Element} this
7559          */
7560         setBottom : function(bottom){
7561             this.setStyle("bottom", this.addUnits(bottom));
7562             return this;
7563         },
7564
7565         /**
7566          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7567          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7568          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7569          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7570          * @return {Roo.Element} this
7571          */
7572         setXY : function(pos, animate){
7573             if(!animate || !A){
7574                 D.setXY(this.dom, pos);
7575             }else{
7576                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7577             }
7578             return this;
7579         },
7580
7581         /**
7582          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7583          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7584          * @param {Number} x X value for new position (coordinates are page-based)
7585          * @param {Number} y Y value for new position (coordinates are page-based)
7586          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7587          * @return {Roo.Element} this
7588          */
7589         setLocation : function(x, y, animate){
7590             this.setXY([x, y], this.preanim(arguments, 2));
7591             return this;
7592         },
7593
7594         /**
7595          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7596          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7597          * @param {Number} x X value for new position (coordinates are page-based)
7598          * @param {Number} y Y value for new position (coordinates are page-based)
7599          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7600          * @return {Roo.Element} this
7601          */
7602         moveTo : function(x, y, animate){
7603             this.setXY([x, y], this.preanim(arguments, 2));
7604             return this;
7605         },
7606
7607         /**
7608          * Returns the region of the given element.
7609          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7610          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7611          */
7612         getRegion : function(){
7613             return D.getRegion(this.dom);
7614         },
7615
7616         /**
7617          * Returns the offset height of the element
7618          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7619          * @return {Number} The element's height
7620          */
7621         getHeight : function(contentHeight){
7622             var h = this.dom.offsetHeight || 0;
7623             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7624         },
7625
7626         /**
7627          * Returns the offset width of the element
7628          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7629          * @return {Number} The element's width
7630          */
7631         getWidth : function(contentWidth){
7632             var w = this.dom.offsetWidth || 0;
7633             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7634         },
7635
7636         /**
7637          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7638          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7639          * if a height has not been set using CSS.
7640          * @return {Number}
7641          */
7642         getComputedHeight : function(){
7643             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7644             if(!h){
7645                 h = parseInt(this.getStyle('height'), 10) || 0;
7646                 if(!this.isBorderBox()){
7647                     h += this.getFrameWidth('tb');
7648                 }
7649             }
7650             return h;
7651         },
7652
7653         /**
7654          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7655          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7656          * if a width has not been set using CSS.
7657          * @return {Number}
7658          */
7659         getComputedWidth : function(){
7660             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7661             if(!w){
7662                 w = parseInt(this.getStyle('width'), 10) || 0;
7663                 if(!this.isBorderBox()){
7664                     w += this.getFrameWidth('lr');
7665                 }
7666             }
7667             return w;
7668         },
7669
7670         /**
7671          * Returns the size of the element.
7672          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7673          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7674          */
7675         getSize : function(contentSize){
7676             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7677         },
7678
7679         /**
7680          * Returns the width and height of the viewport.
7681          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7682          */
7683         getViewSize : function(){
7684             var d = this.dom, doc = document, aw = 0, ah = 0;
7685             if(d == doc || d == doc.body){
7686                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7687             }else{
7688                 return {
7689                     width : d.clientWidth,
7690                     height: d.clientHeight
7691                 };
7692             }
7693         },
7694
7695         /**
7696          * Returns the value of the "value" attribute
7697          * @param {Boolean} asNumber true to parse the value as a number
7698          * @return {String/Number}
7699          */
7700         getValue : function(asNumber){
7701             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7702         },
7703
7704         // private
7705         adjustWidth : function(width){
7706             if(typeof width == "number"){
7707                 if(this.autoBoxAdjust && !this.isBorderBox()){
7708                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7709                 }
7710                 if(width < 0){
7711                     width = 0;
7712                 }
7713             }
7714             return width;
7715         },
7716
7717         // private
7718         adjustHeight : function(height){
7719             if(typeof height == "number"){
7720                if(this.autoBoxAdjust && !this.isBorderBox()){
7721                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7722                }
7723                if(height < 0){
7724                    height = 0;
7725                }
7726             }
7727             return height;
7728         },
7729
7730         /**
7731          * Set the width of the element
7732          * @param {Number} width The new width
7733          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7734          * @return {Roo.Element} this
7735          */
7736         setWidth : function(width, animate){
7737             width = this.adjustWidth(width);
7738             if(!animate || !A){
7739                 this.dom.style.width = this.addUnits(width);
7740             }else{
7741                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Set the height of the element
7748          * @param {Number} height The new height
7749          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7750          * @return {Roo.Element} this
7751          */
7752          setHeight : function(height, animate){
7753             height = this.adjustHeight(height);
7754             if(!animate || !A){
7755                 this.dom.style.height = this.addUnits(height);
7756             }else{
7757                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7758             }
7759             return this;
7760         },
7761
7762         /**
7763          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7764          * @param {Number} width The new width
7765          * @param {Number} height The new height
7766          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7767          * @return {Roo.Element} this
7768          */
7769          setSize : function(width, height, animate){
7770             if(typeof width == "object"){ // in case of object from getSize()
7771                 height = width.height; width = width.width;
7772             }
7773             width = this.adjustWidth(width); height = this.adjustHeight(height);
7774             if(!animate || !A){
7775                 this.dom.style.width = this.addUnits(width);
7776                 this.dom.style.height = this.addUnits(height);
7777             }else{
7778                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7779             }
7780             return this;
7781         },
7782
7783         /**
7784          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7785          * @param {Number} x X value for new position (coordinates are page-based)
7786          * @param {Number} y Y value for new position (coordinates are page-based)
7787          * @param {Number} width The new width
7788          * @param {Number} height The new height
7789          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7790          * @return {Roo.Element} this
7791          */
7792         setBounds : function(x, y, width, height, animate){
7793             if(!animate || !A){
7794                 this.setSize(width, height);
7795                 this.setLocation(x, y);
7796             }else{
7797                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7798                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7799                               this.preanim(arguments, 4), 'motion');
7800             }
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7806          * @param {Roo.lib.Region} region The region to fill
7807          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7808          * @return {Roo.Element} this
7809          */
7810         setRegion : function(region, animate){
7811             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7812             return this;
7813         },
7814
7815         /**
7816          * Appends an event handler
7817          *
7818          * @param {String}   eventName     The type of event to append
7819          * @param {Function} fn        The method the event invokes
7820          * @param {Object} scope       (optional) The scope (this object) of the fn
7821          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7822          */
7823         addListener : function(eventName, fn, scope, options){
7824             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7825         },
7826
7827         /**
7828          * Removes an event handler from this element
7829          * @param {String} eventName the type of event to remove
7830          * @param {Function} fn the method the event invokes
7831          * @return {Roo.Element} this
7832          */
7833         removeListener : function(eventName, fn){
7834             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7835             return this;
7836         },
7837
7838         /**
7839          * Removes all previous added listeners from this element
7840          * @return {Roo.Element} this
7841          */
7842         removeAllListeners : function(){
7843             E.purgeElement(this.dom);
7844             return this;
7845         },
7846
7847         relayEvent : function(eventName, observable){
7848             this.on(eventName, function(e){
7849                 observable.fireEvent(eventName, e);
7850             });
7851         },
7852
7853         /**
7854          * Set the opacity of the element
7855          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7856          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7857          * @return {Roo.Element} this
7858          */
7859          setOpacity : function(opacity, animate){
7860             if(!animate || !A){
7861                 var s = this.dom.style;
7862                 if(Roo.isIE){
7863                     s.zoom = 1;
7864                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7865                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7866                 }else{
7867                     s.opacity = opacity;
7868                 }
7869             }else{
7870                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7871             }
7872             return this;
7873         },
7874
7875         /**
7876          * Gets the left X coordinate
7877          * @param {Boolean} local True to get the local css position instead of page coordinate
7878          * @return {Number}
7879          */
7880         getLeft : function(local){
7881             if(!local){
7882                 return this.getX();
7883             }else{
7884                 return parseInt(this.getStyle("left"), 10) || 0;
7885             }
7886         },
7887
7888         /**
7889          * Gets the right X coordinate of the element (element X position + element width)
7890          * @param {Boolean} local True to get the local css position instead of page coordinate
7891          * @return {Number}
7892          */
7893         getRight : function(local){
7894             if(!local){
7895                 return this.getX() + this.getWidth();
7896             }else{
7897                 return (this.getLeft(true) + this.getWidth()) || 0;
7898             }
7899         },
7900
7901         /**
7902          * Gets the top Y coordinate
7903          * @param {Boolean} local True to get the local css position instead of page coordinate
7904          * @return {Number}
7905          */
7906         getTop : function(local) {
7907             if(!local){
7908                 return this.getY();
7909             }else{
7910                 return parseInt(this.getStyle("top"), 10) || 0;
7911             }
7912         },
7913
7914         /**
7915          * Gets the bottom Y coordinate of the element (element Y position + element height)
7916          * @param {Boolean} local True to get the local css position instead of page coordinate
7917          * @return {Number}
7918          */
7919         getBottom : function(local){
7920             if(!local){
7921                 return this.getY() + this.getHeight();
7922             }else{
7923                 return (this.getTop(true) + this.getHeight()) || 0;
7924             }
7925         },
7926
7927         /**
7928         * Initializes positioning on this element. If a desired position is not passed, it will make the
7929         * the element positioned relative IF it is not already positioned.
7930         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7931         * @param {Number} zIndex (optional) The zIndex to apply
7932         * @param {Number} x (optional) Set the page X position
7933         * @param {Number} y (optional) Set the page Y position
7934         */
7935         position : function(pos, zIndex, x, y){
7936             if(!pos){
7937                if(this.getStyle('position') == 'static'){
7938                    this.setStyle('position', 'relative');
7939                }
7940             }else{
7941                 this.setStyle("position", pos);
7942             }
7943             if(zIndex){
7944                 this.setStyle("z-index", zIndex);
7945             }
7946             if(x !== undefined && y !== undefined){
7947                 this.setXY([x, y]);
7948             }else if(x !== undefined){
7949                 this.setX(x);
7950             }else if(y !== undefined){
7951                 this.setY(y);
7952             }
7953         },
7954
7955         /**
7956         * Clear positioning back to the default when the document was loaded
7957         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7958         * @return {Roo.Element} this
7959          */
7960         clearPositioning : function(value){
7961             value = value ||'';
7962             this.setStyle({
7963                 "left": value,
7964                 "right": value,
7965                 "top": value,
7966                 "bottom": value,
7967                 "z-index": "",
7968                 "position" : "static"
7969             });
7970             return this;
7971         },
7972
7973         /**
7974         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7975         * snapshot before performing an update and then restoring the element.
7976         * @return {Object}
7977         */
7978         getPositioning : function(){
7979             var l = this.getStyle("left");
7980             var t = this.getStyle("top");
7981             return {
7982                 "position" : this.getStyle("position"),
7983                 "left" : l,
7984                 "right" : l ? "" : this.getStyle("right"),
7985                 "top" : t,
7986                 "bottom" : t ? "" : this.getStyle("bottom"),
7987                 "z-index" : this.getStyle("z-index")
7988             };
7989         },
7990
7991         /**
7992          * Gets the width of the border(s) for the specified side(s)
7993          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7994          * passing lr would get the border (l)eft width + the border (r)ight width.
7995          * @return {Number} The width of the sides passed added together
7996          */
7997         getBorderWidth : function(side){
7998             return this.addStyles(side, El.borders);
7999         },
8000
8001         /**
8002          * Gets the width of the padding(s) for the specified side(s)
8003          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8004          * passing lr would get the padding (l)eft + the padding (r)ight.
8005          * @return {Number} The padding of the sides passed added together
8006          */
8007         getPadding : function(side){
8008             return this.addStyles(side, El.paddings);
8009         },
8010
8011         /**
8012         * Set positioning with an object returned by getPositioning().
8013         * @param {Object} posCfg
8014         * @return {Roo.Element} this
8015          */
8016         setPositioning : function(pc){
8017             this.applyStyles(pc);
8018             if(pc.right == "auto"){
8019                 this.dom.style.right = "";
8020             }
8021             if(pc.bottom == "auto"){
8022                 this.dom.style.bottom = "";
8023             }
8024             return this;
8025         },
8026
8027         // private
8028         fixDisplay : function(){
8029             if(this.getStyle("display") == "none"){
8030                 this.setStyle("visibility", "hidden");
8031                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8032                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8033                     this.setStyle("display", "block");
8034                 }
8035             }
8036         },
8037
8038         /**
8039          * Quick set left and top adding default units
8040          * @param {String} left The left CSS property value
8041          * @param {String} top The top CSS property value
8042          * @return {Roo.Element} this
8043          */
8044          setLeftTop : function(left, top){
8045             this.dom.style.left = this.addUnits(left);
8046             this.dom.style.top = this.addUnits(top);
8047             return this;
8048         },
8049
8050         /**
8051          * Move this element relative to its current position.
8052          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8053          * @param {Number} distance How far to move the element in pixels
8054          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8055          * @return {Roo.Element} this
8056          */
8057          move : function(direction, distance, animate){
8058             var xy = this.getXY();
8059             direction = direction.toLowerCase();
8060             switch(direction){
8061                 case "l":
8062                 case "left":
8063                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8064                     break;
8065                case "r":
8066                case "right":
8067                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8068                     break;
8069                case "t":
8070                case "top":
8071                case "up":
8072                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8073                     break;
8074                case "b":
8075                case "bottom":
8076                case "down":
8077                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8078                     break;
8079             }
8080             return this;
8081         },
8082
8083         /**
8084          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8085          * @return {Roo.Element} this
8086          */
8087         clip : function(){
8088             if(!this.isClipped){
8089                this.isClipped = true;
8090                this.originalClip = {
8091                    "o": this.getStyle("overflow"),
8092                    "x": this.getStyle("overflow-x"),
8093                    "y": this.getStyle("overflow-y")
8094                };
8095                this.setStyle("overflow", "hidden");
8096                this.setStyle("overflow-x", "hidden");
8097                this.setStyle("overflow-y", "hidden");
8098             }
8099             return this;
8100         },
8101
8102         /**
8103          *  Return clipping (overflow) to original clipping before clip() was called
8104          * @return {Roo.Element} this
8105          */
8106         unclip : function(){
8107             if(this.isClipped){
8108                 this.isClipped = false;
8109                 var o = this.originalClip;
8110                 if(o.o){this.setStyle("overflow", o.o);}
8111                 if(o.x){this.setStyle("overflow-x", o.x);}
8112                 if(o.y){this.setStyle("overflow-y", o.y);}
8113             }
8114             return this;
8115         },
8116
8117
8118         /**
8119          * Gets the x,y coordinates specified by the anchor position on the element.
8120          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8121          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8122          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8123          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8124          * @return {Array} [x, y] An array containing the element's x and y coordinates
8125          */
8126         getAnchorXY : function(anchor, local, s){
8127             //Passing a different size is useful for pre-calculating anchors,
8128             //especially for anchored animations that change the el size.
8129
8130             var w, h, vp = false;
8131             if(!s){
8132                 var d = this.dom;
8133                 if(d == document.body || d == document){
8134                     vp = true;
8135                     w = D.getViewWidth(); h = D.getViewHeight();
8136                 }else{
8137                     w = this.getWidth(); h = this.getHeight();
8138                 }
8139             }else{
8140                 w = s.width;  h = s.height;
8141             }
8142             var x = 0, y = 0, r = Math.round;
8143             switch((anchor || "tl").toLowerCase()){
8144                 case "c":
8145                     x = r(w*.5);
8146                     y = r(h*.5);
8147                 break;
8148                 case "t":
8149                     x = r(w*.5);
8150                     y = 0;
8151                 break;
8152                 case "l":
8153                     x = 0;
8154                     y = r(h*.5);
8155                 break;
8156                 case "r":
8157                     x = w;
8158                     y = r(h*.5);
8159                 break;
8160                 case "b":
8161                     x = r(w*.5);
8162                     y = h;
8163                 break;
8164                 case "tl":
8165                     x = 0;
8166                     y = 0;
8167                 break;
8168                 case "bl":
8169                     x = 0;
8170                     y = h;
8171                 break;
8172                 case "br":
8173                     x = w;
8174                     y = h;
8175                 break;
8176                 case "tr":
8177                     x = w;
8178                     y = 0;
8179                 break;
8180             }
8181             if(local === true){
8182                 return [x, y];
8183             }
8184             if(vp){
8185                 var sc = this.getScroll();
8186                 return [x + sc.left, y + sc.top];
8187             }
8188             //Add the element's offset xy
8189             var o = this.getXY();
8190             return [x+o[0], y+o[1]];
8191         },
8192
8193         /**
8194          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8195          * supported position values.
8196          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8197          * @param {String} position The position to align to.
8198          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8199          * @return {Array} [x, y]
8200          */
8201         getAlignToXY : function(el, p, o){
8202             el = Roo.get(el);
8203             var d = this.dom;
8204             if(!el.dom){
8205                 throw "Element.alignTo with an element that doesn't exist";
8206             }
8207             var c = false; //constrain to viewport
8208             var p1 = "", p2 = "";
8209             o = o || [0,0];
8210
8211             if(!p){
8212                 p = "tl-bl";
8213             }else if(p == "?"){
8214                 p = "tl-bl?";
8215             }else if(p.indexOf("-") == -1){
8216                 p = "tl-" + p;
8217             }
8218             p = p.toLowerCase();
8219             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8220             if(!m){
8221                throw "Element.alignTo with an invalid alignment " + p;
8222             }
8223             p1 = m[1]; p2 = m[2]; c = !!m[3];
8224
8225             //Subtract the aligned el's internal xy from the target's offset xy
8226             //plus custom offset to get the aligned el's new offset xy
8227             var a1 = this.getAnchorXY(p1, true);
8228             var a2 = el.getAnchorXY(p2, false);
8229             var x = a2[0] - a1[0] + o[0];
8230             var y = a2[1] - a1[1] + o[1];
8231             if(c){
8232                 //constrain the aligned el to viewport if necessary
8233                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8234                 // 5px of margin for ie
8235                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8236
8237                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8238                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8239                 //otherwise swap the aligned el to the opposite border of the target.
8240                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8241                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8242                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8243                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8244
8245                var doc = document;
8246                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8247                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8248
8249                if((x+w) > dw + scrollX){
8250                     x = swapX ? r.left-w : dw+scrollX-w;
8251                 }
8252                if(x < scrollX){
8253                    x = swapX ? r.right : scrollX;
8254                }
8255                if((y+h) > dh + scrollY){
8256                     y = swapY ? r.top-h : dh+scrollY-h;
8257                 }
8258                if (y < scrollY){
8259                    y = swapY ? r.bottom : scrollY;
8260                }
8261             }
8262             return [x,y];
8263         },
8264
8265         // private
8266         getConstrainToXY : function(){
8267             var os = {top:0, left:0, bottom:0, right: 0};
8268
8269             return function(el, local, offsets, proposedXY){
8270                 el = Roo.get(el);
8271                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8272
8273                 var vw, vh, vx = 0, vy = 0;
8274                 if(el.dom == document.body || el.dom == document){
8275                     vw = Roo.lib.Dom.getViewWidth();
8276                     vh = Roo.lib.Dom.getViewHeight();
8277                 }else{
8278                     vw = el.dom.clientWidth;
8279                     vh = el.dom.clientHeight;
8280                     if(!local){
8281                         var vxy = el.getXY();
8282                         vx = vxy[0];
8283                         vy = vxy[1];
8284                     }
8285                 }
8286
8287                 var s = el.getScroll();
8288
8289                 vx += offsets.left + s.left;
8290                 vy += offsets.top + s.top;
8291
8292                 vw -= offsets.right;
8293                 vh -= offsets.bottom;
8294
8295                 var vr = vx+vw;
8296                 var vb = vy+vh;
8297
8298                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8299                 var x = xy[0], y = xy[1];
8300                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8301
8302                 // only move it if it needs it
8303                 var moved = false;
8304
8305                 // first validate right/bottom
8306                 if((x + w) > vr){
8307                     x = vr - w;
8308                     moved = true;
8309                 }
8310                 if((y + h) > vb){
8311                     y = vb - h;
8312                     moved = true;
8313                 }
8314                 // then make sure top/left isn't negative
8315                 if(x < vx){
8316                     x = vx;
8317                     moved = true;
8318                 }
8319                 if(y < vy){
8320                     y = vy;
8321                     moved = true;
8322                 }
8323                 return moved ? [x, y] : false;
8324             };
8325         }(),
8326
8327         // private
8328         adjustForConstraints : function(xy, parent, offsets){
8329             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8330         },
8331
8332         /**
8333          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8334          * document it aligns it to the viewport.
8335          * The position parameter is optional, and can be specified in any one of the following formats:
8336          * <ul>
8337          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8338          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8339          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8340          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8341          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8342          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8343          * </ul>
8344          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8345          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8346          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8347          * that specified in order to enforce the viewport constraints.
8348          * Following are all of the supported anchor positions:
8349     <pre>
8350     Value  Description
8351     -----  -----------------------------
8352     tl     The top left corner (default)
8353     t      The center of the top edge
8354     tr     The top right corner
8355     l      The center of the left edge
8356     c      In the center of the element
8357     r      The center of the right edge
8358     bl     The bottom left corner
8359     b      The center of the bottom edge
8360     br     The bottom right corner
8361     </pre>
8362     Example Usage:
8363     <pre><code>
8364     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8365     el.alignTo("other-el");
8366
8367     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8368     el.alignTo("other-el", "tr?");
8369
8370     // align the bottom right corner of el with the center left edge of other-el
8371     el.alignTo("other-el", "br-l?");
8372
8373     // align the center of el with the bottom left corner of other-el and
8374     // adjust the x position by -6 pixels (and the y position by 0)
8375     el.alignTo("other-el", "c-bl", [-6, 0]);
8376     </code></pre>
8377          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8378          * @param {String} position The position to align to.
8379          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8380          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8381          * @return {Roo.Element} this
8382          */
8383         alignTo : function(element, position, offsets, animate){
8384             var xy = this.getAlignToXY(element, position, offsets);
8385             this.setXY(xy, this.preanim(arguments, 3));
8386             return this;
8387         },
8388
8389         /**
8390          * Anchors an element to another element and realigns it when the window is resized.
8391          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8392          * @param {String} position The position to align to.
8393          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8394          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8395          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8396          * is a number, it is used as the buffer delay (defaults to 50ms).
8397          * @param {Function} callback The function to call after the animation finishes
8398          * @return {Roo.Element} this
8399          */
8400         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8401             var action = function(){
8402                 this.alignTo(el, alignment, offsets, animate);
8403                 Roo.callback(callback, this);
8404             };
8405             Roo.EventManager.onWindowResize(action, this);
8406             var tm = typeof monitorScroll;
8407             if(tm != 'undefined'){
8408                 Roo.EventManager.on(window, 'scroll', action, this,
8409                     {buffer: tm == 'number' ? monitorScroll : 50});
8410             }
8411             action.call(this); // align immediately
8412             return this;
8413         },
8414         /**
8415          * Clears any opacity settings from this element. Required in some cases for IE.
8416          * @return {Roo.Element} this
8417          */
8418         clearOpacity : function(){
8419             if (window.ActiveXObject) {
8420                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8421                     this.dom.style.filter = "";
8422                 }
8423             } else {
8424                 this.dom.style.opacity = "";
8425                 this.dom.style["-moz-opacity"] = "";
8426                 this.dom.style["-khtml-opacity"] = "";
8427             }
8428             return this;
8429         },
8430
8431         /**
8432          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8433          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8434          * @return {Roo.Element} this
8435          */
8436         hide : function(animate){
8437             this.setVisible(false, this.preanim(arguments, 0));
8438             return this;
8439         },
8440
8441         /**
8442         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8443         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8444          * @return {Roo.Element} this
8445          */
8446         show : function(animate){
8447             this.setVisible(true, this.preanim(arguments, 0));
8448             return this;
8449         },
8450
8451         /**
8452          * @private Test if size has a unit, otherwise appends the default
8453          */
8454         addUnits : function(size){
8455             return Roo.Element.addUnits(size, this.defaultUnit);
8456         },
8457
8458         /**
8459          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8460          * @return {Roo.Element} this
8461          */
8462         beginMeasure : function(){
8463             var el = this.dom;
8464             if(el.offsetWidth || el.offsetHeight){
8465                 return this; // offsets work already
8466             }
8467             var changed = [];
8468             var p = this.dom, b = document.body; // start with this element
8469             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8470                 var pe = Roo.get(p);
8471                 if(pe.getStyle('display') == 'none'){
8472                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8473                     p.style.visibility = "hidden";
8474                     p.style.display = "block";
8475                 }
8476                 p = p.parentNode;
8477             }
8478             this._measureChanged = changed;
8479             return this;
8480
8481         },
8482
8483         /**
8484          * Restores displays to before beginMeasure was called
8485          * @return {Roo.Element} this
8486          */
8487         endMeasure : function(){
8488             var changed = this._measureChanged;
8489             if(changed){
8490                 for(var i = 0, len = changed.length; i < len; i++) {
8491                     var r = changed[i];
8492                     r.el.style.visibility = r.visibility;
8493                     r.el.style.display = "none";
8494                 }
8495                 this._measureChanged = null;
8496             }
8497             return this;
8498         },
8499
8500         /**
8501         * Update the innerHTML of this element, optionally searching for and processing scripts
8502         * @param {String} html The new HTML
8503         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8504         * @param {Function} callback For async script loading you can be noticed when the update completes
8505         * @return {Roo.Element} this
8506          */
8507         update : function(html, loadScripts, callback){
8508             if(typeof html == "undefined"){
8509                 html = "";
8510             }
8511             if(loadScripts !== true){
8512                 this.dom.innerHTML = html;
8513                 if(typeof callback == "function"){
8514                     callback();
8515                 }
8516                 return this;
8517             }
8518             var id = Roo.id();
8519             var dom = this.dom;
8520
8521             html += '<span id="' + id + '"></span>';
8522
8523             E.onAvailable(id, function(){
8524                 var hd = document.getElementsByTagName("head")[0];
8525                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8526                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8527                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8528
8529                 var match;
8530                 while(match = re.exec(html)){
8531                     var attrs = match[1];
8532                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8533                     if(srcMatch && srcMatch[2]){
8534                        var s = document.createElement("script");
8535                        s.src = srcMatch[2];
8536                        var typeMatch = attrs.match(typeRe);
8537                        if(typeMatch && typeMatch[2]){
8538                            s.type = typeMatch[2];
8539                        }
8540                        hd.appendChild(s);
8541                     }else if(match[2] && match[2].length > 0){
8542                         if(window.execScript) {
8543                            window.execScript(match[2]);
8544                         } else {
8545                             /**
8546                              * eval:var:id
8547                              * eval:var:dom
8548                              * eval:var:html
8549                              * 
8550                              */
8551                            window.eval(match[2]);
8552                         }
8553                     }
8554                 }
8555                 var el = document.getElementById(id);
8556                 if(el){el.parentNode.removeChild(el);}
8557                 if(typeof callback == "function"){
8558                     callback();
8559                 }
8560             });
8561             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8562             return this;
8563         },
8564
8565         /**
8566          * Direct access to the UpdateManager update() method (takes the same parameters).
8567          * @param {String/Function} url The url for this request or a function to call to get the url
8568          * @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}
8569          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8570          * @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.
8571          * @return {Roo.Element} this
8572          */
8573         load : function(){
8574             var um = this.getUpdateManager();
8575             um.update.apply(um, arguments);
8576             return this;
8577         },
8578
8579         /**
8580         * Gets this element's UpdateManager
8581         * @return {Roo.UpdateManager} The UpdateManager
8582         */
8583         getUpdateManager : function(){
8584             if(!this.updateManager){
8585                 this.updateManager = new Roo.UpdateManager(this);
8586             }
8587             return this.updateManager;
8588         },
8589
8590         /**
8591          * Disables text selection for this element (normalized across browsers)
8592          * @return {Roo.Element} this
8593          */
8594         unselectable : function(){
8595             this.dom.unselectable = "on";
8596             this.swallowEvent("selectstart", true);
8597             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8598             this.addClass("x-unselectable");
8599             return this;
8600         },
8601
8602         /**
8603         * Calculates the x, y to center this element on the screen
8604         * @return {Array} The x, y values [x, y]
8605         */
8606         getCenterXY : function(){
8607             return this.getAlignToXY(document, 'c-c');
8608         },
8609
8610         /**
8611         * Centers the Element in either the viewport, or another Element.
8612         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8613         */
8614         center : function(centerIn){
8615             this.alignTo(centerIn || document, 'c-c');
8616             return this;
8617         },
8618
8619         /**
8620          * Tests various css rules/browsers to determine if this element uses a border box
8621          * @return {Boolean}
8622          */
8623         isBorderBox : function(){
8624             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8625         },
8626
8627         /**
8628          * Return a box {x, y, width, height} that can be used to set another elements
8629          * size/location to match this element.
8630          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8631          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8632          * @return {Object} box An object in the format {x, y, width, height}
8633          */
8634         getBox : function(contentBox, local){
8635             var xy;
8636             if(!local){
8637                 xy = this.getXY();
8638             }else{
8639                 var left = parseInt(this.getStyle("left"), 10) || 0;
8640                 var top = parseInt(this.getStyle("top"), 10) || 0;
8641                 xy = [left, top];
8642             }
8643             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8644             if(!contentBox){
8645                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8646             }else{
8647                 var l = this.getBorderWidth("l")+this.getPadding("l");
8648                 var r = this.getBorderWidth("r")+this.getPadding("r");
8649                 var t = this.getBorderWidth("t")+this.getPadding("t");
8650                 var b = this.getBorderWidth("b")+this.getPadding("b");
8651                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8652             }
8653             bx.right = bx.x + bx.width;
8654             bx.bottom = bx.y + bx.height;
8655             return bx;
8656         },
8657
8658         /**
8659          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8660          for more information about the sides.
8661          * @param {String} sides
8662          * @return {Number}
8663          */
8664         getFrameWidth : function(sides, onlyContentBox){
8665             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8666         },
8667
8668         /**
8669          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8670          * @param {Object} box The box to fill {x, y, width, height}
8671          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8672          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8673          * @return {Roo.Element} this
8674          */
8675         setBox : function(box, adjust, animate){
8676             var w = box.width, h = box.height;
8677             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8678                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8679                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8680             }
8681             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8682             return this;
8683         },
8684
8685         /**
8686          * Forces the browser to repaint this element
8687          * @return {Roo.Element} this
8688          */
8689          repaint : function(){
8690             var dom = this.dom;
8691             this.addClass("x-repaint");
8692             setTimeout(function(){
8693                 Roo.get(dom).removeClass("x-repaint");
8694             }, 1);
8695             return this;
8696         },
8697
8698         /**
8699          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8700          * then it returns the calculated width of the sides (see getPadding)
8701          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8702          * @return {Object/Number}
8703          */
8704         getMargins : function(side){
8705             if(!side){
8706                 return {
8707                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8708                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8709                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8710                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8711                 };
8712             }else{
8713                 return this.addStyles(side, El.margins);
8714              }
8715         },
8716
8717         // private
8718         addStyles : function(sides, styles){
8719             var val = 0, v, w;
8720             for(var i = 0, len = sides.length; i < len; i++){
8721                 v = this.getStyle(styles[sides.charAt(i)]);
8722                 if(v){
8723                      w = parseInt(v, 10);
8724                      if(w){ val += w; }
8725                 }
8726             }
8727             return val;
8728         },
8729
8730         /**
8731          * Creates a proxy element of this element
8732          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8733          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8734          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8735          * @return {Roo.Element} The new proxy element
8736          */
8737         createProxy : function(config, renderTo, matchBox){
8738             if(renderTo){
8739                 renderTo = Roo.getDom(renderTo);
8740             }else{
8741                 renderTo = document.body;
8742             }
8743             config = typeof config == "object" ?
8744                 config : {tag : "div", cls: config};
8745             var proxy = Roo.DomHelper.append(renderTo, config, true);
8746             if(matchBox){
8747                proxy.setBox(this.getBox());
8748             }
8749             return proxy;
8750         },
8751
8752         /**
8753          * Puts a mask over this element to disable user interaction. Requires core.css.
8754          * This method can only be applied to elements which accept child nodes.
8755          * @param {String} msg (optional) A message to display in the mask
8756          * @param {String} msgCls (optional) A css class to apply to the msg element
8757          * @return {Element} The mask  element
8758          */
8759         mask : function(msg, msgCls){
8760             if(this.getStyle("position") == "static"){
8761                 this.setStyle("position", "relative");
8762             }
8763             if(!this._mask){
8764                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8765             }
8766             this.addClass("x-masked");
8767             this._mask.setDisplayed(true);
8768             if(typeof msg == 'string'){
8769                 if(!this._maskMsg){
8770                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8771                 }
8772                 var mm = this._maskMsg;
8773                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8774                 mm.dom.firstChild.innerHTML = msg;
8775                 mm.setDisplayed(true);
8776                 mm.center(this);
8777             }
8778             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8779                 this._mask.setHeight(this.getHeight());
8780             }
8781             return this._mask;
8782         },
8783
8784         /**
8785          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8786          * it is cached for reuse.
8787          */
8788         unmask : function(removeEl){
8789             if(this._mask){
8790                 if(removeEl === true){
8791                     this._mask.remove();
8792                     delete this._mask;
8793                     if(this._maskMsg){
8794                         this._maskMsg.remove();
8795                         delete this._maskMsg;
8796                     }
8797                 }else{
8798                     this._mask.setDisplayed(false);
8799                     if(this._maskMsg){
8800                         this._maskMsg.setDisplayed(false);
8801                     }
8802                 }
8803             }
8804             this.removeClass("x-masked");
8805         },
8806
8807         /**
8808          * Returns true if this element is masked
8809          * @return {Boolean}
8810          */
8811         isMasked : function(){
8812             return this._mask && this._mask.isVisible();
8813         },
8814
8815         /**
8816          * Creates an iframe shim for this element to keep selects and other windowed objects from
8817          * showing through.
8818          * @return {Roo.Element} The new shim element
8819          */
8820         createShim : function(){
8821             var el = document.createElement('iframe');
8822             el.frameBorder = 'no';
8823             el.className = 'roo-shim';
8824             if(Roo.isIE && Roo.isSecure){
8825                 el.src = Roo.SSL_SECURE_URL;
8826             }
8827             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8828             shim.autoBoxAdjust = false;
8829             return shim;
8830         },
8831
8832         /**
8833          * Removes this element from the DOM and deletes it from the cache
8834          */
8835         remove : function(){
8836             if(this.dom.parentNode){
8837                 this.dom.parentNode.removeChild(this.dom);
8838             }
8839             delete El.cache[this.dom.id];
8840         },
8841
8842         /**
8843          * Sets up event handlers to add and remove a css class when the mouse is over this element
8844          * @param {String} className
8845          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8846          * mouseout events for children elements
8847          * @return {Roo.Element} this
8848          */
8849         addClassOnOver : function(className, preventFlicker){
8850             this.on("mouseover", function(){
8851                 Roo.fly(this, '_internal').addClass(className);
8852             }, this.dom);
8853             var removeFn = function(e){
8854                 if(preventFlicker !== true || !e.within(this, true)){
8855                     Roo.fly(this, '_internal').removeClass(className);
8856                 }
8857             };
8858             this.on("mouseout", removeFn, this.dom);
8859             return this;
8860         },
8861
8862         /**
8863          * Sets up event handlers to add and remove a css class when this element has the focus
8864          * @param {String} className
8865          * @return {Roo.Element} this
8866          */
8867         addClassOnFocus : function(className){
8868             this.on("focus", function(){
8869                 Roo.fly(this, '_internal').addClass(className);
8870             }, this.dom);
8871             this.on("blur", function(){
8872                 Roo.fly(this, '_internal').removeClass(className);
8873             }, this.dom);
8874             return this;
8875         },
8876         /**
8877          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8878          * @param {String} className
8879          * @return {Roo.Element} this
8880          */
8881         addClassOnClick : function(className){
8882             var dom = this.dom;
8883             this.on("mousedown", function(){
8884                 Roo.fly(dom, '_internal').addClass(className);
8885                 var d = Roo.get(document);
8886                 var fn = function(){
8887                     Roo.fly(dom, '_internal').removeClass(className);
8888                     d.removeListener("mouseup", fn);
8889                 };
8890                 d.on("mouseup", fn);
8891             });
8892             return this;
8893         },
8894
8895         /**
8896          * Stops the specified event from bubbling and optionally prevents the default action
8897          * @param {String} eventName
8898          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8899          * @return {Roo.Element} this
8900          */
8901         swallowEvent : function(eventName, preventDefault){
8902             var fn = function(e){
8903                 e.stopPropagation();
8904                 if(preventDefault){
8905                     e.preventDefault();
8906                 }
8907             };
8908             if(eventName instanceof Array){
8909                 for(var i = 0, len = eventName.length; i < len; i++){
8910                      this.on(eventName[i], fn);
8911                 }
8912                 return this;
8913             }
8914             this.on(eventName, fn);
8915             return this;
8916         },
8917
8918         /**
8919          * @private
8920          */
8921       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8922
8923         /**
8924          * Sizes this element to its parent element's dimensions performing
8925          * neccessary box adjustments.
8926          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8927          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8928          * @return {Roo.Element} this
8929          */
8930         fitToParent : function(monitorResize, targetParent) {
8931           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8932           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8933           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8934             return;
8935           }
8936           var p = Roo.get(targetParent || this.dom.parentNode);
8937           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8938           if (monitorResize === true) {
8939             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8940             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8941           }
8942           return this;
8943         },
8944
8945         /**
8946          * Gets the next sibling, skipping text nodes
8947          * @return {HTMLElement} The next sibling or null
8948          */
8949         getNextSibling : function(){
8950             var n = this.dom.nextSibling;
8951             while(n && n.nodeType != 1){
8952                 n = n.nextSibling;
8953             }
8954             return n;
8955         },
8956
8957         /**
8958          * Gets the previous sibling, skipping text nodes
8959          * @return {HTMLElement} The previous sibling or null
8960          */
8961         getPrevSibling : function(){
8962             var n = this.dom.previousSibling;
8963             while(n && n.nodeType != 1){
8964                 n = n.previousSibling;
8965             }
8966             return n;
8967         },
8968
8969
8970         /**
8971          * Appends the passed element(s) to this element
8972          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8973          * @return {Roo.Element} this
8974          */
8975         appendChild: function(el){
8976             el = Roo.get(el);
8977             el.appendTo(this);
8978             return this;
8979         },
8980
8981         /**
8982          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8983          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8984          * automatically generated with the specified attributes.
8985          * @param {HTMLElement} insertBefore (optional) a child element of this element
8986          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8987          * @return {Roo.Element} The new child element
8988          */
8989         createChild: function(config, insertBefore, returnDom){
8990             config = config || {tag:'div'};
8991             if(insertBefore){
8992                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8993             }
8994             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8995         },
8996
8997         /**
8998          * Appends this element to the passed element
8999          * @param {String/HTMLElement/Element} el The new parent element
9000          * @return {Roo.Element} this
9001          */
9002         appendTo: function(el){
9003             el = Roo.getDom(el);
9004             el.appendChild(this.dom);
9005             return this;
9006         },
9007
9008         /**
9009          * Inserts this element before the passed element in the DOM
9010          * @param {String/HTMLElement/Element} el The element to insert before
9011          * @return {Roo.Element} this
9012          */
9013         insertBefore: function(el){
9014             el = Roo.getDom(el);
9015             el.parentNode.insertBefore(this.dom, el);
9016             return this;
9017         },
9018
9019         /**
9020          * Inserts this element after the passed element in the DOM
9021          * @param {String/HTMLElement/Element} el The element to insert after
9022          * @return {Roo.Element} this
9023          */
9024         insertAfter: function(el){
9025             el = Roo.getDom(el);
9026             el.parentNode.insertBefore(this.dom, el.nextSibling);
9027             return this;
9028         },
9029
9030         /**
9031          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9032          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9033          * @return {Roo.Element} The new child
9034          */
9035         insertFirst: function(el, returnDom){
9036             el = el || {};
9037             if(typeof el == 'object' && !el.nodeType){ // dh config
9038                 return this.createChild(el, this.dom.firstChild, returnDom);
9039             }else{
9040                 el = Roo.getDom(el);
9041                 this.dom.insertBefore(el, this.dom.firstChild);
9042                 return !returnDom ? Roo.get(el) : el;
9043             }
9044         },
9045
9046         /**
9047          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9048          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9049          * @param {String} where (optional) 'before' or 'after' defaults to before
9050          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9051          * @return {Roo.Element} the inserted Element
9052          */
9053         insertSibling: function(el, where, returnDom){
9054             where = where ? where.toLowerCase() : 'before';
9055             el = el || {};
9056             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9057
9058             if(typeof el == 'object' && !el.nodeType){ // dh config
9059                 if(where == 'after' && !this.dom.nextSibling){
9060                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9061                 }else{
9062                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9063                 }
9064
9065             }else{
9066                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9067                             where == 'before' ? this.dom : this.dom.nextSibling);
9068                 if(!returnDom){
9069                     rt = Roo.get(rt);
9070                 }
9071             }
9072             return rt;
9073         },
9074
9075         /**
9076          * Creates and wraps this element with another element
9077          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9078          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9079          * @return {HTMLElement/Element} The newly created wrapper element
9080          */
9081         wrap: function(config, returnDom){
9082             if(!config){
9083                 config = {tag: "div"};
9084             }
9085             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9086             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9087             return newEl;
9088         },
9089
9090         /**
9091          * Replaces the passed element with this element
9092          * @param {String/HTMLElement/Element} el The element to replace
9093          * @return {Roo.Element} this
9094          */
9095         replace: function(el){
9096             el = Roo.get(el);
9097             this.insertBefore(el);
9098             el.remove();
9099             return this;
9100         },
9101
9102         /**
9103          * Inserts an html fragment into this element
9104          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9105          * @param {String} html The HTML fragment
9106          * @param {Boolean} returnEl True to return an Roo.Element
9107          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9108          */
9109         insertHtml : function(where, html, returnEl){
9110             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9111             return returnEl ? Roo.get(el) : el;
9112         },
9113
9114         /**
9115          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9116          * @param {Object} o The object with the attributes
9117          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9118          * @return {Roo.Element} this
9119          */
9120         set : function(o, useSet){
9121             var el = this.dom;
9122             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9123             for(var attr in o){
9124                 if(attr == "style" || typeof o[attr] == "function") continue;
9125                 if(attr=="cls"){
9126                     el.className = o["cls"];
9127                 }else{
9128                     if(useSet) el.setAttribute(attr, o[attr]);
9129                     else el[attr] = o[attr];
9130                 }
9131             }
9132             if(o.style){
9133                 Roo.DomHelper.applyStyles(el, o.style);
9134             }
9135             return this;
9136         },
9137
9138         /**
9139          * Convenience method for constructing a KeyMap
9140          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9141          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9142          * @param {Function} fn The function to call
9143          * @param {Object} scope (optional) The scope of the function
9144          * @return {Roo.KeyMap} The KeyMap created
9145          */
9146         addKeyListener : function(key, fn, scope){
9147             var config;
9148             if(typeof key != "object" || key instanceof Array){
9149                 config = {
9150                     key: key,
9151                     fn: fn,
9152                     scope: scope
9153                 };
9154             }else{
9155                 config = {
9156                     key : key.key,
9157                     shift : key.shift,
9158                     ctrl : key.ctrl,
9159                     alt : key.alt,
9160                     fn: fn,
9161                     scope: scope
9162                 };
9163             }
9164             return new Roo.KeyMap(this, config);
9165         },
9166
9167         /**
9168          * Creates a KeyMap for this element
9169          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9170          * @return {Roo.KeyMap} The KeyMap created
9171          */
9172         addKeyMap : function(config){
9173             return new Roo.KeyMap(this, config);
9174         },
9175
9176         /**
9177          * Returns true if this element is scrollable.
9178          * @return {Boolean}
9179          */
9180          isScrollable : function(){
9181             var dom = this.dom;
9182             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9183         },
9184
9185         /**
9186          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9187          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9188          * @param {Number} value The new scroll value
9189          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9190          * @return {Element} this
9191          */
9192
9193         scrollTo : function(side, value, animate){
9194             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9195             if(!animate || !A){
9196                 this.dom[prop] = value;
9197             }else{
9198                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9199                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9200             }
9201             return this;
9202         },
9203
9204         /**
9205          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9206          * within this element's scrollable range.
9207          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9208          * @param {Number} distance How far to scroll the element in pixels
9209          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9210          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9211          * was scrolled as far as it could go.
9212          */
9213          scroll : function(direction, distance, animate){
9214              if(!this.isScrollable()){
9215                  return;
9216              }
9217              var el = this.dom;
9218              var l = el.scrollLeft, t = el.scrollTop;
9219              var w = el.scrollWidth, h = el.scrollHeight;
9220              var cw = el.clientWidth, ch = el.clientHeight;
9221              direction = direction.toLowerCase();
9222              var scrolled = false;
9223              var a = this.preanim(arguments, 2);
9224              switch(direction){
9225                  case "l":
9226                  case "left":
9227                      if(w - l > cw){
9228                          var v = Math.min(l + distance, w-cw);
9229                          this.scrollTo("left", v, a);
9230                          scrolled = true;
9231                      }
9232                      break;
9233                 case "r":
9234                 case "right":
9235                      if(l > 0){
9236                          var v = Math.max(l - distance, 0);
9237                          this.scrollTo("left", v, a);
9238                          scrolled = true;
9239                      }
9240                      break;
9241                 case "t":
9242                 case "top":
9243                 case "up":
9244                      if(t > 0){
9245                          var v = Math.max(t - distance, 0);
9246                          this.scrollTo("top", v, a);
9247                          scrolled = true;
9248                      }
9249                      break;
9250                 case "b":
9251                 case "bottom":
9252                 case "down":
9253                      if(h - t > ch){
9254                          var v = Math.min(t + distance, h-ch);
9255                          this.scrollTo("top", v, a);
9256                          scrolled = true;
9257                      }
9258                      break;
9259              }
9260              return scrolled;
9261         },
9262
9263         /**
9264          * Translates the passed page coordinates into left/top css values for this element
9265          * @param {Number/Array} x The page x or an array containing [x, y]
9266          * @param {Number} y The page y
9267          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9268          */
9269         translatePoints : function(x, y){
9270             if(typeof x == 'object' || x instanceof Array){
9271                 y = x[1]; x = x[0];
9272             }
9273             var p = this.getStyle('position');
9274             var o = this.getXY();
9275
9276             var l = parseInt(this.getStyle('left'), 10);
9277             var t = parseInt(this.getStyle('top'), 10);
9278
9279             if(isNaN(l)){
9280                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9281             }
9282             if(isNaN(t)){
9283                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9284             }
9285
9286             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9287         },
9288
9289         /**
9290          * Returns the current scroll position of the element.
9291          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9292          */
9293         getScroll : function(){
9294             var d = this.dom, doc = document;
9295             if(d == doc || d == doc.body){
9296                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9297                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9298                 return {left: l, top: t};
9299             }else{
9300                 return {left: d.scrollLeft, top: d.scrollTop};
9301             }
9302         },
9303
9304         /**
9305          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9306          * are convert to standard 6 digit hex color.
9307          * @param {String} attr The css attribute
9308          * @param {String} defaultValue The default value to use when a valid color isn't found
9309          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9310          * YUI color anims.
9311          */
9312         getColor : function(attr, defaultValue, prefix){
9313             var v = this.getStyle(attr);
9314             if(!v || v == "transparent" || v == "inherit") {
9315                 return defaultValue;
9316             }
9317             var color = typeof prefix == "undefined" ? "#" : prefix;
9318             if(v.substr(0, 4) == "rgb("){
9319                 var rvs = v.slice(4, v.length -1).split(",");
9320                 for(var i = 0; i < 3; i++){
9321                     var h = parseInt(rvs[i]).toString(16);
9322                     if(h < 16){
9323                         h = "0" + h;
9324                     }
9325                     color += h;
9326                 }
9327             } else {
9328                 if(v.substr(0, 1) == "#"){
9329                     if(v.length == 4) {
9330                         for(var i = 1; i < 4; i++){
9331                             var c = v.charAt(i);
9332                             color +=  c + c;
9333                         }
9334                     }else if(v.length == 7){
9335                         color += v.substr(1);
9336                     }
9337                 }
9338             }
9339             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9340         },
9341
9342         /**
9343          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9344          * gradient background, rounded corners and a 4-way shadow.
9345          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9346          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9347          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9348          * @return {Roo.Element} this
9349          */
9350         boxWrap : function(cls){
9351             cls = cls || 'x-box';
9352             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9353             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9354             return el;
9355         },
9356
9357         /**
9358          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9359          * @param {String} namespace The namespace in which to look for the attribute
9360          * @param {String} name The attribute name
9361          * @return {String} The attribute value
9362          */
9363         getAttributeNS : Roo.isIE ? function(ns, name){
9364             var d = this.dom;
9365             var type = typeof d[ns+":"+name];
9366             if(type != 'undefined' && type != 'unknown'){
9367                 return d[ns+":"+name];
9368             }
9369             return d[name];
9370         } : function(ns, name){
9371             var d = this.dom;
9372             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9373         }
9374     };
9375
9376     var ep = El.prototype;
9377
9378     /**
9379      * Appends an event handler (Shorthand for addListener)
9380      * @param {String}   eventName     The type of event to append
9381      * @param {Function} fn        The method the event invokes
9382      * @param {Object} scope       (optional) The scope (this object) of the fn
9383      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9384      * @method
9385      */
9386     ep.on = ep.addListener;
9387         // backwards compat
9388     ep.mon = ep.addListener;
9389
9390     /**
9391      * Removes an event handler from this element (shorthand for removeListener)
9392      * @param {String} eventName the type of event to remove
9393      * @param {Function} fn the method the event invokes
9394      * @return {Roo.Element} this
9395      * @method
9396      */
9397     ep.un = ep.removeListener;
9398
9399     /**
9400      * true to automatically adjust width and height settings for box-model issues (default to true)
9401      */
9402     ep.autoBoxAdjust = true;
9403
9404     // private
9405     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9406
9407     // private
9408     El.addUnits = function(v, defaultUnit){
9409         if(v === "" || v == "auto"){
9410             return v;
9411         }
9412         if(v === undefined){
9413             return '';
9414         }
9415         if(typeof v == "number" || !El.unitPattern.test(v)){
9416             return v + (defaultUnit || 'px');
9417         }
9418         return v;
9419     };
9420
9421     // special markup used throughout Roo when box wrapping elements
9422     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9423     /**
9424      * Visibility mode constant - Use visibility to hide element
9425      * @static
9426      * @type Number
9427      */
9428     El.VISIBILITY = 1;
9429     /**
9430      * Visibility mode constant - Use display to hide element
9431      * @static
9432      * @type Number
9433      */
9434     El.DISPLAY = 2;
9435
9436     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9437     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9438     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9439
9440
9441
9442     /**
9443      * @private
9444      */
9445     El.cache = {};
9446
9447     var docEl;
9448
9449     /**
9450      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9451      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9452      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9453      * @return {Element} The Element object
9454      * @static
9455      */
9456     El.get = function(el){
9457         var ex, elm, id;
9458         if(!el){ return null; }
9459         if(typeof el == "string"){ // element id
9460             if(!(elm = document.getElementById(el))){
9461                 return null;
9462             }
9463             if(ex = El.cache[el]){
9464                 ex.dom = elm;
9465             }else{
9466                 ex = El.cache[el] = new El(elm);
9467             }
9468             return ex;
9469         }else if(el.tagName){ // dom element
9470             if(!(id = el.id)){
9471                 id = Roo.id(el);
9472             }
9473             if(ex = El.cache[id]){
9474                 ex.dom = el;
9475             }else{
9476                 ex = El.cache[id] = new El(el);
9477             }
9478             return ex;
9479         }else if(el instanceof El){
9480             if(el != docEl){
9481                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9482                                                               // catch case where it hasn't been appended
9483                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9484             }
9485             return el;
9486         }else if(el.isComposite){
9487             return el;
9488         }else if(el instanceof Array){
9489             return El.select(el);
9490         }else if(el == document){
9491             // create a bogus element object representing the document object
9492             if(!docEl){
9493                 var f = function(){};
9494                 f.prototype = El.prototype;
9495                 docEl = new f();
9496                 docEl.dom = document;
9497             }
9498             return docEl;
9499         }
9500         return null;
9501     };
9502
9503     // private
9504     El.uncache = function(el){
9505         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9506             if(a[i]){
9507                 delete El.cache[a[i].id || a[i]];
9508             }
9509         }
9510     };
9511
9512     // private
9513     // Garbage collection - uncache elements/purge listeners on orphaned elements
9514     // so we don't hold a reference and cause the browser to retain them
9515     El.garbageCollect = function(){
9516         if(!Roo.enableGarbageCollector){
9517             clearInterval(El.collectorThread);
9518             return;
9519         }
9520         for(var eid in El.cache){
9521             var el = El.cache[eid], d = el.dom;
9522             // -------------------------------------------------------
9523             // Determining what is garbage:
9524             // -------------------------------------------------------
9525             // !d
9526             // dom node is null, definitely garbage
9527             // -------------------------------------------------------
9528             // !d.parentNode
9529             // no parentNode == direct orphan, definitely garbage
9530             // -------------------------------------------------------
9531             // !d.offsetParent && !document.getElementById(eid)
9532             // display none elements have no offsetParent so we will
9533             // also try to look it up by it's id. However, check
9534             // offsetParent first so we don't do unneeded lookups.
9535             // This enables collection of elements that are not orphans
9536             // directly, but somewhere up the line they have an orphan
9537             // parent.
9538             // -------------------------------------------------------
9539             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9540                 delete El.cache[eid];
9541                 if(d && Roo.enableListenerCollection){
9542                     E.purgeElement(d);
9543                 }
9544             }
9545         }
9546     }
9547     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9548
9549
9550     // dom is optional
9551     El.Flyweight = function(dom){
9552         this.dom = dom;
9553     };
9554     El.Flyweight.prototype = El.prototype;
9555
9556     El._flyweights = {};
9557     /**
9558      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9559      * the dom node can be overwritten by other code.
9560      * @param {String/HTMLElement} el The dom node or id
9561      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9562      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9563      * @static
9564      * @return {Element} The shared Element object
9565      */
9566     El.fly = function(el, named){
9567         named = named || '_global';
9568         el = Roo.getDom(el);
9569         if(!el){
9570             return null;
9571         }
9572         if(!El._flyweights[named]){
9573             El._flyweights[named] = new El.Flyweight();
9574         }
9575         El._flyweights[named].dom = el;
9576         return El._flyweights[named];
9577     };
9578
9579     /**
9580      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9581      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9582      * Shorthand of {@link Roo.Element#get}
9583      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9584      * @return {Element} The Element object
9585      * @member Roo
9586      * @method get
9587      */
9588     Roo.get = El.get;
9589     /**
9590      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9591      * the dom node can be overwritten by other code.
9592      * Shorthand of {@link Roo.Element#fly}
9593      * @param {String/HTMLElement} el The dom node or id
9594      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9595      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9596      * @static
9597      * @return {Element} The shared Element object
9598      * @member Roo
9599      * @method fly
9600      */
9601     Roo.fly = El.fly;
9602
9603     // speedy lookup for elements never to box adjust
9604     var noBoxAdjust = Roo.isStrict ? {
9605         select:1
9606     } : {
9607         input:1, select:1, textarea:1
9608     };
9609     if(Roo.isIE || Roo.isGecko){
9610         noBoxAdjust['button'] = 1;
9611     }
9612
9613
9614     Roo.EventManager.on(window, 'unload', function(){
9615         delete El.cache;
9616         delete El._flyweights;
9617     });
9618 })();
9619
9620
9621
9622
9623 if(Roo.DomQuery){
9624     Roo.Element.selectorFunction = Roo.DomQuery.select;
9625 }
9626
9627 Roo.Element.select = function(selector, unique, root){
9628     var els;
9629     if(typeof selector == "string"){
9630         els = Roo.Element.selectorFunction(selector, root);
9631     }else if(selector.length !== undefined){
9632         els = selector;
9633     }else{
9634         throw "Invalid selector";
9635     }
9636     if(unique === true){
9637         return new Roo.CompositeElement(els);
9638     }else{
9639         return new Roo.CompositeElementLite(els);
9640     }
9641 };
9642 /**
9643  * Selects elements based on the passed CSS selector to enable working on them as 1.
9644  * @param {String/Array} selector The CSS selector or an array of elements
9645  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9646  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9647  * @return {CompositeElementLite/CompositeElement}
9648  * @member Roo
9649  * @method select
9650  */
9651 Roo.select = Roo.Element.select;
9652
9653
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663
9664
9665
9666 /*
9667  * Based on:
9668  * Ext JS Library 1.1.1
9669  * Copyright(c) 2006-2007, Ext JS, LLC.
9670  *
9671  * Originally Released Under LGPL - original licence link has changed is not relivant.
9672  *
9673  * Fork - LGPL
9674  * <script type="text/javascript">
9675  */
9676
9677
9678
9679 //Notifies Element that fx methods are available
9680 Roo.enableFx = true;
9681
9682 /**
9683  * @class Roo.Fx
9684  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9685  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9686  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9687  * Element effects to work.</p><br/>
9688  *
9689  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9690  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9691  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9692  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9693  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9694  * expected results and should be done with care.</p><br/>
9695  *
9696  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9697  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9698 <pre>
9699 Value  Description
9700 -----  -----------------------------
9701 tl     The top left corner
9702 t      The center of the top edge
9703 tr     The top right corner
9704 l      The center of the left edge
9705 r      The center of the right edge
9706 bl     The bottom left corner
9707 b      The center of the bottom edge
9708 br     The bottom right corner
9709 </pre>
9710  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9711  * below are common options that can be passed to any Fx method.</b>
9712  * @cfg {Function} callback A function called when the effect is finished
9713  * @cfg {Object} scope The scope of the effect function
9714  * @cfg {String} easing A valid Easing value for the effect
9715  * @cfg {String} afterCls A css class to apply after the effect
9716  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9717  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9718  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9719  * effects that end with the element being visually hidden, ignored otherwise)
9720  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9721  * a function which returns such a specification that will be applied to the Element after the effect finishes
9722  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9723  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9724  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9725  */
9726 Roo.Fx = {
9727         /**
9728          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9729          * origin for the slide effect.  This function automatically handles wrapping the element with
9730          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9731          * Usage:
9732          *<pre><code>
9733 // default: slide the element in from the top
9734 el.slideIn();
9735
9736 // custom: slide the element in from the right with a 2-second duration
9737 el.slideIn('r', { duration: 2 });
9738
9739 // common config options shown with default values
9740 el.slideIn('t', {
9741     easing: 'easeOut',
9742     duration: .5
9743 });
9744 </code></pre>
9745          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9746          * @param {Object} options (optional) Object literal with any of the Fx config options
9747          * @return {Roo.Element} The Element
9748          */
9749     slideIn : function(anchor, o){
9750         var el = this.getFxEl();
9751         o = o || {};
9752
9753         el.queueFx(o, function(){
9754
9755             anchor = anchor || "t";
9756
9757             // fix display to visibility
9758             this.fixDisplay();
9759
9760             // restore values after effect
9761             var r = this.getFxRestore();
9762             var b = this.getBox();
9763             // fixed size for slide
9764             this.setSize(b);
9765
9766             // wrap if needed
9767             var wrap = this.fxWrap(r.pos, o, "hidden");
9768
9769             var st = this.dom.style;
9770             st.visibility = "visible";
9771             st.position = "absolute";
9772
9773             // clear out temp styles after slide and unwrap
9774             var after = function(){
9775                 el.fxUnwrap(wrap, r.pos, o);
9776                 st.width = r.width;
9777                 st.height = r.height;
9778                 el.afterFx(o);
9779             };
9780             // time to calc the positions
9781             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9782
9783             switch(anchor.toLowerCase()){
9784                 case "t":
9785                     wrap.setSize(b.width, 0);
9786                     st.left = st.bottom = "0";
9787                     a = {height: bh};
9788                 break;
9789                 case "l":
9790                     wrap.setSize(0, b.height);
9791                     st.right = st.top = "0";
9792                     a = {width: bw};
9793                 break;
9794                 case "r":
9795                     wrap.setSize(0, b.height);
9796                     wrap.setX(b.right);
9797                     st.left = st.top = "0";
9798                     a = {width: bw, points: pt};
9799                 break;
9800                 case "b":
9801                     wrap.setSize(b.width, 0);
9802                     wrap.setY(b.bottom);
9803                     st.left = st.top = "0";
9804                     a = {height: bh, points: pt};
9805                 break;
9806                 case "tl":
9807                     wrap.setSize(0, 0);
9808                     st.right = st.bottom = "0";
9809                     a = {width: bw, height: bh};
9810                 break;
9811                 case "bl":
9812                     wrap.setSize(0, 0);
9813                     wrap.setY(b.y+b.height);
9814                     st.right = st.top = "0";
9815                     a = {width: bw, height: bh, points: pt};
9816                 break;
9817                 case "br":
9818                     wrap.setSize(0, 0);
9819                     wrap.setXY([b.right, b.bottom]);
9820                     st.left = st.top = "0";
9821                     a = {width: bw, height: bh, points: pt};
9822                 break;
9823                 case "tr":
9824                     wrap.setSize(0, 0);
9825                     wrap.setX(b.x+b.width);
9826                     st.left = st.bottom = "0";
9827                     a = {width: bw, height: bh, points: pt};
9828                 break;
9829             }
9830             this.dom.style.visibility = "visible";
9831             wrap.show();
9832
9833             arguments.callee.anim = wrap.fxanim(a,
9834                 o,
9835                 'motion',
9836                 .5,
9837                 'easeOut', after);
9838         });
9839         return this;
9840     },
9841     
9842         /**
9843          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9844          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9845          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9846          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9847          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9848          * Usage:
9849          *<pre><code>
9850 // default: slide the element out to the top
9851 el.slideOut();
9852
9853 // custom: slide the element out to the right with a 2-second duration
9854 el.slideOut('r', { duration: 2 });
9855
9856 // common config options shown with default values
9857 el.slideOut('t', {
9858     easing: 'easeOut',
9859     duration: .5,
9860     remove: false,
9861     useDisplay: false
9862 });
9863 </code></pre>
9864          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9865          * @param {Object} options (optional) Object literal with any of the Fx config options
9866          * @return {Roo.Element} The Element
9867          */
9868     slideOut : function(anchor, o){
9869         var el = this.getFxEl();
9870         o = o || {};
9871
9872         el.queueFx(o, function(){
9873
9874             anchor = anchor || "t";
9875
9876             // restore values after effect
9877             var r = this.getFxRestore();
9878             
9879             var b = this.getBox();
9880             // fixed size for slide
9881             this.setSize(b);
9882
9883             // wrap if needed
9884             var wrap = this.fxWrap(r.pos, o, "visible");
9885
9886             var st = this.dom.style;
9887             st.visibility = "visible";
9888             st.position = "absolute";
9889
9890             wrap.setSize(b);
9891
9892             var after = function(){
9893                 if(o.useDisplay){
9894                     el.setDisplayed(false);
9895                 }else{
9896                     el.hide();
9897                 }
9898
9899                 el.fxUnwrap(wrap, r.pos, o);
9900
9901                 st.width = r.width;
9902                 st.height = r.height;
9903
9904                 el.afterFx(o);
9905             };
9906
9907             var a, zero = {to: 0};
9908             switch(anchor.toLowerCase()){
9909                 case "t":
9910                     st.left = st.bottom = "0";
9911                     a = {height: zero};
9912                 break;
9913                 case "l":
9914                     st.right = st.top = "0";
9915                     a = {width: zero};
9916                 break;
9917                 case "r":
9918                     st.left = st.top = "0";
9919                     a = {width: zero, points: {to:[b.right, b.y]}};
9920                 break;
9921                 case "b":
9922                     st.left = st.top = "0";
9923                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9924                 break;
9925                 case "tl":
9926                     st.right = st.bottom = "0";
9927                     a = {width: zero, height: zero};
9928                 break;
9929                 case "bl":
9930                     st.right = st.top = "0";
9931                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9932                 break;
9933                 case "br":
9934                     st.left = st.top = "0";
9935                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9936                 break;
9937                 case "tr":
9938                     st.left = st.bottom = "0";
9939                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9940                 break;
9941             }
9942
9943             arguments.callee.anim = wrap.fxanim(a,
9944                 o,
9945                 'motion',
9946                 .5,
9947                 "easeOut", after);
9948         });
9949         return this;
9950     },
9951
9952         /**
9953          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9954          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9955          * The element must be removed from the DOM using the 'remove' config option if desired.
9956          * Usage:
9957          *<pre><code>
9958 // default
9959 el.puff();
9960
9961 // common config options shown with default values
9962 el.puff({
9963     easing: 'easeOut',
9964     duration: .5,
9965     remove: false,
9966     useDisplay: false
9967 });
9968 </code></pre>
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     puff : function(o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977             this.clearOpacity();
9978             this.show();
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             var st = this.dom.style;
9983
9984             var after = function(){
9985                 if(o.useDisplay){
9986                     el.setDisplayed(false);
9987                 }else{
9988                     el.hide();
9989                 }
9990
9991                 el.clearOpacity();
9992
9993                 el.setPositioning(r.pos);
9994                 st.width = r.width;
9995                 st.height = r.height;
9996                 st.fontSize = '';
9997                 el.afterFx(o);
9998             };
9999
10000             var width = this.getWidth();
10001             var height = this.getHeight();
10002
10003             arguments.callee.anim = this.fxanim({
10004                     width : {to: this.adjustWidth(width * 2)},
10005                     height : {to: this.adjustHeight(height * 2)},
10006                     points : {by: [-(width * .5), -(height * .5)]},
10007                     opacity : {to: 0},
10008                     fontSize: {to:200, unit: "%"}
10009                 },
10010                 o,
10011                 'motion',
10012                 .5,
10013                 "easeOut", after);
10014         });
10015         return this;
10016     },
10017
10018         /**
10019          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10020          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10021          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10022          * Usage:
10023          *<pre><code>
10024 // default
10025 el.switchOff();
10026
10027 // all config options shown with default values
10028 el.switchOff({
10029     easing: 'easeIn',
10030     duration: .3,
10031     remove: false,
10032     useDisplay: false
10033 });
10034 </code></pre>
10035          * @param {Object} options (optional) Object literal with any of the Fx config options
10036          * @return {Roo.Element} The Element
10037          */
10038     switchOff : function(o){
10039         var el = this.getFxEl();
10040         o = o || {};
10041
10042         el.queueFx(o, function(){
10043             this.clearOpacity();
10044             this.clip();
10045
10046             // restore values after effect
10047             var r = this.getFxRestore();
10048             var st = this.dom.style;
10049
10050             var after = function(){
10051                 if(o.useDisplay){
10052                     el.setDisplayed(false);
10053                 }else{
10054                     el.hide();
10055                 }
10056
10057                 el.clearOpacity();
10058                 el.setPositioning(r.pos);
10059                 st.width = r.width;
10060                 st.height = r.height;
10061
10062                 el.afterFx(o);
10063             };
10064
10065             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10066                 this.clearOpacity();
10067                 (function(){
10068                     this.fxanim({
10069                         height:{to:1},
10070                         points:{by:[0, this.getHeight() * .5]}
10071                     }, o, 'motion', 0.3, 'easeIn', after);
10072                 }).defer(100, this);
10073             });
10074         });
10075         return this;
10076     },
10077
10078     /**
10079      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10080      * changed using the "attr" config option) and then fading back to the original color. If no original
10081      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10082      * Usage:
10083 <pre><code>
10084 // default: highlight background to yellow
10085 el.highlight();
10086
10087 // custom: highlight foreground text to blue for 2 seconds
10088 el.highlight("0000ff", { attr: 'color', duration: 2 });
10089
10090 // common config options shown with default values
10091 el.highlight("ffff9c", {
10092     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10093     endColor: (current color) or "ffffff",
10094     easing: 'easeIn',
10095     duration: 1
10096 });
10097 </code></pre>
10098      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10099      * @param {Object} options (optional) Object literal with any of the Fx config options
10100      * @return {Roo.Element} The Element
10101      */ 
10102     highlight : function(color, o){
10103         var el = this.getFxEl();
10104         o = o || {};
10105
10106         el.queueFx(o, function(){
10107             color = color || "ffff9c";
10108             attr = o.attr || "backgroundColor";
10109
10110             this.clearOpacity();
10111             this.show();
10112
10113             var origColor = this.getColor(attr);
10114             var restoreColor = this.dom.style[attr];
10115             endColor = (o.endColor || origColor) || "ffffff";
10116
10117             var after = function(){
10118                 el.dom.style[attr] = restoreColor;
10119                 el.afterFx(o);
10120             };
10121
10122             var a = {};
10123             a[attr] = {from: color, to: endColor};
10124             arguments.callee.anim = this.fxanim(a,
10125                 o,
10126                 'color',
10127                 1,
10128                 'easeIn', after);
10129         });
10130         return this;
10131     },
10132
10133    /**
10134     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10135     * Usage:
10136 <pre><code>
10137 // default: a single light blue ripple
10138 el.frame();
10139
10140 // custom: 3 red ripples lasting 3 seconds total
10141 el.frame("ff0000", 3, { duration: 3 });
10142
10143 // common config options shown with default values
10144 el.frame("C3DAF9", 1, {
10145     duration: 1 //duration of entire animation (not each individual ripple)
10146     // Note: Easing is not configurable and will be ignored if included
10147 });
10148 </code></pre>
10149     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10150     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10151     * @param {Object} options (optional) Object literal with any of the Fx config options
10152     * @return {Roo.Element} The Element
10153     */
10154     frame : function(color, count, o){
10155         var el = this.getFxEl();
10156         o = o || {};
10157
10158         el.queueFx(o, function(){
10159             color = color || "#C3DAF9";
10160             if(color.length == 6){
10161                 color = "#" + color;
10162             }
10163             count = count || 1;
10164             duration = o.duration || 1;
10165             this.show();
10166
10167             var b = this.getBox();
10168             var animFn = function(){
10169                 var proxy = this.createProxy({
10170
10171                      style:{
10172                         visbility:"hidden",
10173                         position:"absolute",
10174                         "z-index":"35000", // yee haw
10175                         border:"0px solid " + color
10176                      }
10177                   });
10178                 var scale = Roo.isBorderBox ? 2 : 1;
10179                 proxy.animate({
10180                     top:{from:b.y, to:b.y - 20},
10181                     left:{from:b.x, to:b.x - 20},
10182                     borderWidth:{from:0, to:10},
10183                     opacity:{from:1, to:0},
10184                     height:{from:b.height, to:(b.height + (20*scale))},
10185                     width:{from:b.width, to:(b.width + (20*scale))}
10186                 }, duration, function(){
10187                     proxy.remove();
10188                 });
10189                 if(--count > 0){
10190                      animFn.defer((duration/2)*1000, this);
10191                 }else{
10192                     el.afterFx(o);
10193                 }
10194             };
10195             animFn.call(this);
10196         });
10197         return this;
10198     },
10199
10200    /**
10201     * Creates a pause before any subsequent queued effects begin.  If there are
10202     * no effects queued after the pause it will have no effect.
10203     * Usage:
10204 <pre><code>
10205 el.pause(1);
10206 </code></pre>
10207     * @param {Number} seconds The length of time to pause (in seconds)
10208     * @return {Roo.Element} The Element
10209     */
10210     pause : function(seconds){
10211         var el = this.getFxEl();
10212         var o = {};
10213
10214         el.queueFx(o, function(){
10215             setTimeout(function(){
10216                 el.afterFx(o);
10217             }, seconds * 1000);
10218         });
10219         return this;
10220     },
10221
10222    /**
10223     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10224     * using the "endOpacity" config option.
10225     * Usage:
10226 <pre><code>
10227 // default: fade in from opacity 0 to 100%
10228 el.fadeIn();
10229
10230 // custom: fade in from opacity 0 to 75% over 2 seconds
10231 el.fadeIn({ endOpacity: .75, duration: 2});
10232
10233 // common config options shown with default values
10234 el.fadeIn({
10235     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10236     easing: 'easeOut',
10237     duration: .5
10238 });
10239 </code></pre>
10240     * @param {Object} options (optional) Object literal with any of the Fx config options
10241     * @return {Roo.Element} The Element
10242     */
10243     fadeIn : function(o){
10244         var el = this.getFxEl();
10245         o = o || {};
10246         el.queueFx(o, function(){
10247             this.setOpacity(0);
10248             this.fixDisplay();
10249             this.dom.style.visibility = 'visible';
10250             var to = o.endOpacity || 1;
10251             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10252                 o, null, .5, "easeOut", function(){
10253                 if(to == 1){
10254                     this.clearOpacity();
10255                 }
10256                 el.afterFx(o);
10257             });
10258         });
10259         return this;
10260     },
10261
10262    /**
10263     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10264     * using the "endOpacity" config option.
10265     * Usage:
10266 <pre><code>
10267 // default: fade out from the element's current opacity to 0
10268 el.fadeOut();
10269
10270 // custom: fade out from the element's current opacity to 25% over 2 seconds
10271 el.fadeOut({ endOpacity: .25, duration: 2});
10272
10273 // common config options shown with default values
10274 el.fadeOut({
10275     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10276     easing: 'easeOut',
10277     duration: .5
10278     remove: false,
10279     useDisplay: false
10280 });
10281 </code></pre>
10282     * @param {Object} options (optional) Object literal with any of the Fx config options
10283     * @return {Roo.Element} The Element
10284     */
10285     fadeOut : function(o){
10286         var el = this.getFxEl();
10287         o = o || {};
10288         el.queueFx(o, function(){
10289             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10290                 o, null, .5, "easeOut", function(){
10291                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10292                      this.dom.style.display = "none";
10293                 }else{
10294                      this.dom.style.visibility = "hidden";
10295                 }
10296                 this.clearOpacity();
10297                 el.afterFx(o);
10298             });
10299         });
10300         return this;
10301     },
10302
10303    /**
10304     * Animates the transition of an element's dimensions from a starting height/width
10305     * to an ending height/width.
10306     * Usage:
10307 <pre><code>
10308 // change height and width to 100x100 pixels
10309 el.scale(100, 100);
10310
10311 // common config options shown with default values.  The height and width will default to
10312 // the element's existing values if passed as null.
10313 el.scale(
10314     [element's width],
10315     [element's height], {
10316     easing: 'easeOut',
10317     duration: .35
10318 });
10319 </code></pre>
10320     * @param {Number} width  The new width (pass undefined to keep the original width)
10321     * @param {Number} height  The new height (pass undefined to keep the original height)
10322     * @param {Object} options (optional) Object literal with any of the Fx config options
10323     * @return {Roo.Element} The Element
10324     */
10325     scale : function(w, h, o){
10326         this.shift(Roo.apply({}, o, {
10327             width: w,
10328             height: h
10329         }));
10330         return this;
10331     },
10332
10333    /**
10334     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10335     * Any of these properties not specified in the config object will not be changed.  This effect 
10336     * requires that at least one new dimension, position or opacity setting must be passed in on
10337     * the config object in order for the function to have any effect.
10338     * Usage:
10339 <pre><code>
10340 // slide the element horizontally to x position 200 while changing the height and opacity
10341 el.shift({ x: 200, height: 50, opacity: .8 });
10342
10343 // common config options shown with default values.
10344 el.shift({
10345     width: [element's width],
10346     height: [element's height],
10347     x: [element's x position],
10348     y: [element's y position],
10349     opacity: [element's opacity],
10350     easing: 'easeOut',
10351     duration: .35
10352 });
10353 </code></pre>
10354     * @param {Object} options  Object literal with any of the Fx config options
10355     * @return {Roo.Element} The Element
10356     */
10357     shift : function(o){
10358         var el = this.getFxEl();
10359         o = o || {};
10360         el.queueFx(o, function(){
10361             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10362             if(w !== undefined){
10363                 a.width = {to: this.adjustWidth(w)};
10364             }
10365             if(h !== undefined){
10366                 a.height = {to: this.adjustHeight(h)};
10367             }
10368             if(x !== undefined || y !== undefined){
10369                 a.points = {to: [
10370                     x !== undefined ? x : this.getX(),
10371                     y !== undefined ? y : this.getY()
10372                 ]};
10373             }
10374             if(op !== undefined){
10375                 a.opacity = {to: op};
10376             }
10377             if(o.xy !== undefined){
10378                 a.points = {to: o.xy};
10379             }
10380             arguments.callee.anim = this.fxanim(a,
10381                 o, 'motion', .35, "easeOut", function(){
10382                 el.afterFx(o);
10383             });
10384         });
10385         return this;
10386     },
10387
10388         /**
10389          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10390          * ending point of the effect.
10391          * Usage:
10392          *<pre><code>
10393 // default: slide the element downward while fading out
10394 el.ghost();
10395
10396 // custom: slide the element out to the right with a 2-second duration
10397 el.ghost('r', { duration: 2 });
10398
10399 // common config options shown with default values
10400 el.ghost('b', {
10401     easing: 'easeOut',
10402     duration: .5
10403     remove: false,
10404     useDisplay: false
10405 });
10406 </code></pre>
10407          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10408          * @param {Object} options (optional) Object literal with any of the Fx config options
10409          * @return {Roo.Element} The Element
10410          */
10411     ghost : function(anchor, o){
10412         var el = this.getFxEl();
10413         o = o || {};
10414
10415         el.queueFx(o, function(){
10416             anchor = anchor || "b";
10417
10418             // restore values after effect
10419             var r = this.getFxRestore();
10420             var w = this.getWidth(),
10421                 h = this.getHeight();
10422
10423             var st = this.dom.style;
10424
10425             var after = function(){
10426                 if(o.useDisplay){
10427                     el.setDisplayed(false);
10428                 }else{
10429                     el.hide();
10430                 }
10431
10432                 el.clearOpacity();
10433                 el.setPositioning(r.pos);
10434                 st.width = r.width;
10435                 st.height = r.height;
10436
10437                 el.afterFx(o);
10438             };
10439
10440             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10441             switch(anchor.toLowerCase()){
10442                 case "t":
10443                     pt.by = [0, -h];
10444                 break;
10445                 case "l":
10446                     pt.by = [-w, 0];
10447                 break;
10448                 case "r":
10449                     pt.by = [w, 0];
10450                 break;
10451                 case "b":
10452                     pt.by = [0, h];
10453                 break;
10454                 case "tl":
10455                     pt.by = [-w, -h];
10456                 break;
10457                 case "bl":
10458                     pt.by = [-w, h];
10459                 break;
10460                 case "br":
10461                     pt.by = [w, h];
10462                 break;
10463                 case "tr":
10464                     pt.by = [w, -h];
10465                 break;
10466             }
10467
10468             arguments.callee.anim = this.fxanim(a,
10469                 o,
10470                 'motion',
10471                 .5,
10472                 "easeOut", after);
10473         });
10474         return this;
10475     },
10476
10477         /**
10478          * Ensures that all effects queued after syncFx is called on the element are
10479          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10480          * @return {Roo.Element} The Element
10481          */
10482     syncFx : function(){
10483         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10484             block : false,
10485             concurrent : true,
10486             stopFx : false
10487         });
10488         return this;
10489     },
10490
10491         /**
10492          * Ensures that all effects queued after sequenceFx is called on the element are
10493          * run in sequence.  This is the opposite of {@link #syncFx}.
10494          * @return {Roo.Element} The Element
10495          */
10496     sequenceFx : function(){
10497         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10498             block : false,
10499             concurrent : false,
10500             stopFx : false
10501         });
10502         return this;
10503     },
10504
10505         /* @private */
10506     nextFx : function(){
10507         var ef = this.fxQueue[0];
10508         if(ef){
10509             ef.call(this);
10510         }
10511     },
10512
10513         /**
10514          * Returns true if the element has any effects actively running or queued, else returns false.
10515          * @return {Boolean} True if element has active effects, else false
10516          */
10517     hasActiveFx : function(){
10518         return this.fxQueue && this.fxQueue[0];
10519     },
10520
10521         /**
10522          * Stops any running effects and clears the element's internal effects queue if it contains
10523          * any additional effects that haven't started yet.
10524          * @return {Roo.Element} The Element
10525          */
10526     stopFx : function(){
10527         if(this.hasActiveFx()){
10528             var cur = this.fxQueue[0];
10529             if(cur && cur.anim && cur.anim.isAnimated()){
10530                 this.fxQueue = [cur]; // clear out others
10531                 cur.anim.stop(true);
10532             }
10533         }
10534         return this;
10535     },
10536
10537         /* @private */
10538     beforeFx : function(o){
10539         if(this.hasActiveFx() && !o.concurrent){
10540            if(o.stopFx){
10541                this.stopFx();
10542                return true;
10543            }
10544            return false;
10545         }
10546         return true;
10547     },
10548
10549         /**
10550          * Returns true if the element is currently blocking so that no other effect can be queued
10551          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10552          * used to ensure that an effect initiated by a user action runs to completion prior to the
10553          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10554          * @return {Boolean} True if blocking, else false
10555          */
10556     hasFxBlock : function(){
10557         var q = this.fxQueue;
10558         return q && q[0] && q[0].block;
10559     },
10560
10561         /* @private */
10562     queueFx : function(o, fn){
10563         if(!this.fxQueue){
10564             this.fxQueue = [];
10565         }
10566         if(!this.hasFxBlock()){
10567             Roo.applyIf(o, this.fxDefaults);
10568             if(!o.concurrent){
10569                 var run = this.beforeFx(o);
10570                 fn.block = o.block;
10571                 this.fxQueue.push(fn);
10572                 if(run){
10573                     this.nextFx();
10574                 }
10575             }else{
10576                 fn.call(this);
10577             }
10578         }
10579         return this;
10580     },
10581
10582         /* @private */
10583     fxWrap : function(pos, o, vis){
10584         var wrap;
10585         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10586             var wrapXY;
10587             if(o.fixPosition){
10588                 wrapXY = this.getXY();
10589             }
10590             var div = document.createElement("div");
10591             div.style.visibility = vis;
10592             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10593             wrap.setPositioning(pos);
10594             if(wrap.getStyle("position") == "static"){
10595                 wrap.position("relative");
10596             }
10597             this.clearPositioning('auto');
10598             wrap.clip();
10599             wrap.dom.appendChild(this.dom);
10600             if(wrapXY){
10601                 wrap.setXY(wrapXY);
10602             }
10603         }
10604         return wrap;
10605     },
10606
10607         /* @private */
10608     fxUnwrap : function(wrap, pos, o){
10609         this.clearPositioning();
10610         this.setPositioning(pos);
10611         if(!o.wrap){
10612             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10613             wrap.remove();
10614         }
10615     },
10616
10617         /* @private */
10618     getFxRestore : function(){
10619         var st = this.dom.style;
10620         return {pos: this.getPositioning(), width: st.width, height : st.height};
10621     },
10622
10623         /* @private */
10624     afterFx : function(o){
10625         if(o.afterStyle){
10626             this.applyStyles(o.afterStyle);
10627         }
10628         if(o.afterCls){
10629             this.addClass(o.afterCls);
10630         }
10631         if(o.remove === true){
10632             this.remove();
10633         }
10634         Roo.callback(o.callback, o.scope, [this]);
10635         if(!o.concurrent){
10636             this.fxQueue.shift();
10637             this.nextFx();
10638         }
10639     },
10640
10641         /* @private */
10642     getFxEl : function(){ // support for composite element fx
10643         return Roo.get(this.dom);
10644     },
10645
10646         /* @private */
10647     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10648         animType = animType || 'run';
10649         opt = opt || {};
10650         var anim = Roo.lib.Anim[animType](
10651             this.dom, args,
10652             (opt.duration || defaultDur) || .35,
10653             (opt.easing || defaultEase) || 'easeOut',
10654             function(){
10655                 Roo.callback(cb, this);
10656             },
10657             this
10658         );
10659         opt.anim = anim;
10660         return anim;
10661     }
10662 };
10663
10664 // backwords compat
10665 Roo.Fx.resize = Roo.Fx.scale;
10666
10667 //When included, Roo.Fx is automatically applied to Element so that all basic
10668 //effects are available directly via the Element API
10669 Roo.apply(Roo.Element.prototype, Roo.Fx);