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