sync
[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
9634 /*
9635  * Based on:
9636  * Ext JS Library 1.1.1
9637  * Copyright(c) 2006-2007, Ext JS, LLC.
9638  *
9639  * Originally Released Under LGPL - original licence link has changed is not relivant.
9640  *
9641  * Fork - LGPL
9642  * <script type="text/javascript">
9643  */
9644
9645
9646
9647 //Notifies Element that fx methods are available
9648 Roo.enableFx = true;
9649
9650 /**
9651  * @class Roo.Fx
9652  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9653  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9654  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9655  * Element effects to work.</p><br/>
9656  *
9657  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9658  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9659  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9660  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9661  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9662  * expected results and should be done with care.</p><br/>
9663  *
9664  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9665  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9666 <pre>
9667 Value  Description
9668 -----  -----------------------------
9669 tl     The top left corner
9670 t      The center of the top edge
9671 tr     The top right corner
9672 l      The center of the left edge
9673 r      The center of the right edge
9674 bl     The bottom left corner
9675 b      The center of the bottom edge
9676 br     The bottom right corner
9677 </pre>
9678  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9679  * below are common options that can be passed to any Fx method.</b>
9680  * @cfg {Function} callback A function called when the effect is finished
9681  * @cfg {Object} scope The scope of the effect function
9682  * @cfg {String} easing A valid Easing value for the effect
9683  * @cfg {String} afterCls A css class to apply after the effect
9684  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9685  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9686  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9687  * effects that end with the element being visually hidden, ignored otherwise)
9688  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9689  * a function which returns such a specification that will be applied to the Element after the effect finishes
9690  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9691  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9692  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9693  */
9694 Roo.Fx = {
9695         /**
9696          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9697          * origin for the slide effect.  This function automatically handles wrapping the element with
9698          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9699          * Usage:
9700          *<pre><code>
9701 // default: slide the element in from the top
9702 el.slideIn();
9703
9704 // custom: slide the element in from the right with a 2-second duration
9705 el.slideIn('r', { duration: 2 });
9706
9707 // common config options shown with default values
9708 el.slideIn('t', {
9709     easing: 'easeOut',
9710     duration: .5
9711 });
9712 </code></pre>
9713          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9714          * @param {Object} options (optional) Object literal with any of the Fx config options
9715          * @return {Roo.Element} The Element
9716          */
9717     slideIn : function(anchor, o){
9718         var el = this.getFxEl();
9719         o = o || {};
9720
9721         el.queueFx(o, function(){
9722
9723             anchor = anchor || "t";
9724
9725             // fix display to visibility
9726             this.fixDisplay();
9727
9728             // restore values after effect
9729             var r = this.getFxRestore();
9730             var b = this.getBox();
9731             // fixed size for slide
9732             this.setSize(b);
9733
9734             // wrap if needed
9735             var wrap = this.fxWrap(r.pos, o, "hidden");
9736
9737             var st = this.dom.style;
9738             st.visibility = "visible";
9739             st.position = "absolute";
9740
9741             // clear out temp styles after slide and unwrap
9742             var after = function(){
9743                 el.fxUnwrap(wrap, r.pos, o);
9744                 st.width = r.width;
9745                 st.height = r.height;
9746                 el.afterFx(o);
9747             };
9748             // time to calc the positions
9749             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9750
9751             switch(anchor.toLowerCase()){
9752                 case "t":
9753                     wrap.setSize(b.width, 0);
9754                     st.left = st.bottom = "0";
9755                     a = {height: bh};
9756                 break;
9757                 case "l":
9758                     wrap.setSize(0, b.height);
9759                     st.right = st.top = "0";
9760                     a = {width: bw};
9761                 break;
9762                 case "r":
9763                     wrap.setSize(0, b.height);
9764                     wrap.setX(b.right);
9765                     st.left = st.top = "0";
9766                     a = {width: bw, points: pt};
9767                 break;
9768                 case "b":
9769                     wrap.setSize(b.width, 0);
9770                     wrap.setY(b.bottom);
9771                     st.left = st.top = "0";
9772                     a = {height: bh, points: pt};
9773                 break;
9774                 case "tl":
9775                     wrap.setSize(0, 0);
9776                     st.right = st.bottom = "0";
9777                     a = {width: bw, height: bh};
9778                 break;
9779                 case "bl":
9780                     wrap.setSize(0, 0);
9781                     wrap.setY(b.y+b.height);
9782                     st.right = st.top = "0";
9783                     a = {width: bw, height: bh, points: pt};
9784                 break;
9785                 case "br":
9786                     wrap.setSize(0, 0);
9787                     wrap.setXY([b.right, b.bottom]);
9788                     st.left = st.top = "0";
9789                     a = {width: bw, height: bh, points: pt};
9790                 break;
9791                 case "tr":
9792                     wrap.setSize(0, 0);
9793                     wrap.setX(b.x+b.width);
9794                     st.left = st.bottom = "0";
9795                     a = {width: bw, height: bh, points: pt};
9796                 break;
9797             }
9798             this.dom.style.visibility = "visible";
9799             wrap.show();
9800
9801             arguments.callee.anim = wrap.fxanim(a,
9802                 o,
9803                 'motion',
9804                 .5,
9805                 'easeOut', after);
9806         });
9807         return this;
9808     },
9809     
9810         /**
9811          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9812          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9813          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9814          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9815          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9816          * Usage:
9817          *<pre><code>
9818 // default: slide the element out to the top
9819 el.slideOut();
9820
9821 // custom: slide the element out to the right with a 2-second duration
9822 el.slideOut('r', { duration: 2 });
9823
9824 // common config options shown with default values
9825 el.slideOut('t', {
9826     easing: 'easeOut',
9827     duration: .5,
9828     remove: false,
9829     useDisplay: false
9830 });
9831 </code></pre>
9832          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9833          * @param {Object} options (optional) Object literal with any of the Fx config options
9834          * @return {Roo.Element} The Element
9835          */
9836     slideOut : function(anchor, o){
9837         var el = this.getFxEl();
9838         o = o || {};
9839
9840         el.queueFx(o, function(){
9841
9842             anchor = anchor || "t";
9843
9844             // restore values after effect
9845             var r = this.getFxRestore();
9846             
9847             var b = this.getBox();
9848             // fixed size for slide
9849             this.setSize(b);
9850
9851             // wrap if needed
9852             var wrap = this.fxWrap(r.pos, o, "visible");
9853
9854             var st = this.dom.style;
9855             st.visibility = "visible";
9856             st.position = "absolute";
9857
9858             wrap.setSize(b);
9859
9860             var after = function(){
9861                 if(o.useDisplay){
9862                     el.setDisplayed(false);
9863                 }else{
9864                     el.hide();
9865                 }
9866
9867                 el.fxUnwrap(wrap, r.pos, o);
9868
9869                 st.width = r.width;
9870                 st.height = r.height;
9871
9872                 el.afterFx(o);
9873             };
9874
9875             var a, zero = {to: 0};
9876             switch(anchor.toLowerCase()){
9877                 case "t":
9878                     st.left = st.bottom = "0";
9879                     a = {height: zero};
9880                 break;
9881                 case "l":
9882                     st.right = st.top = "0";
9883                     a = {width: zero};
9884                 break;
9885                 case "r":
9886                     st.left = st.top = "0";
9887                     a = {width: zero, points: {to:[b.right, b.y]}};
9888                 break;
9889                 case "b":
9890                     st.left = st.top = "0";
9891                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9892                 break;
9893                 case "tl":
9894                     st.right = st.bottom = "0";
9895                     a = {width: zero, height: zero};
9896                 break;
9897                 case "bl":
9898                     st.right = st.top = "0";
9899                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9900                 break;
9901                 case "br":
9902                     st.left = st.top = "0";
9903                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9904                 break;
9905                 case "tr":
9906                     st.left = st.bottom = "0";
9907                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9908                 break;
9909             }
9910
9911             arguments.callee.anim = wrap.fxanim(a,
9912                 o,
9913                 'motion',
9914                 .5,
9915                 "easeOut", after);
9916         });
9917         return this;
9918     },
9919
9920         /**
9921          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9922          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9923          * The element must be removed from the DOM using the 'remove' config option if desired.
9924          * Usage:
9925          *<pre><code>
9926 // default
9927 el.puff();
9928
9929 // common config options shown with default values
9930 el.puff({
9931     easing: 'easeOut',
9932     duration: .5,
9933     remove: false,
9934     useDisplay: false
9935 });
9936 </code></pre>
9937          * @param {Object} options (optional) Object literal with any of the Fx config options
9938          * @return {Roo.Element} The Element
9939          */
9940     puff : function(o){
9941         var el = this.getFxEl();
9942         o = o || {};
9943
9944         el.queueFx(o, function(){
9945             this.clearOpacity();
9946             this.show();
9947
9948             // restore values after effect
9949             var r = this.getFxRestore();
9950             var st = this.dom.style;
9951
9952             var after = function(){
9953                 if(o.useDisplay){
9954                     el.setDisplayed(false);
9955                 }else{
9956                     el.hide();
9957                 }
9958
9959                 el.clearOpacity();
9960
9961                 el.setPositioning(r.pos);
9962                 st.width = r.width;
9963                 st.height = r.height;
9964                 st.fontSize = '';
9965                 el.afterFx(o);
9966             };
9967
9968             var width = this.getWidth();
9969             var height = this.getHeight();
9970
9971             arguments.callee.anim = this.fxanim({
9972                     width : {to: this.adjustWidth(width * 2)},
9973                     height : {to: this.adjustHeight(height * 2)},
9974                     points : {by: [-(width * .5), -(height * .5)]},
9975                     opacity : {to: 0},
9976                     fontSize: {to:200, unit: "%"}
9977                 },
9978                 o,
9979                 'motion',
9980                 .5,
9981                 "easeOut", after);
9982         });
9983         return this;
9984     },
9985
9986         /**
9987          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9988          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9989          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9990          * Usage:
9991          *<pre><code>
9992 // default
9993 el.switchOff();
9994
9995 // all config options shown with default values
9996 el.switchOff({
9997     easing: 'easeIn',
9998     duration: .3,
9999     remove: false,
10000     useDisplay: false
10001 });
10002 </code></pre>
10003          * @param {Object} options (optional) Object literal with any of the Fx config options
10004          * @return {Roo.Element} The Element
10005          */
10006     switchOff : function(o){
10007         var el = this.getFxEl();
10008         o = o || {};
10009
10010         el.queueFx(o, function(){
10011             this.clearOpacity();
10012             this.clip();
10013
10014             // restore values after effect
10015             var r = this.getFxRestore();
10016             var st = this.dom.style;
10017
10018             var after = function(){
10019                 if(o.useDisplay){
10020                     el.setDisplayed(false);
10021                 }else{
10022                     el.hide();
10023                 }
10024
10025                 el.clearOpacity();
10026                 el.setPositioning(r.pos);
10027                 st.width = r.width;
10028                 st.height = r.height;
10029
10030                 el.afterFx(o);
10031             };
10032
10033             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10034                 this.clearOpacity();
10035                 (function(){
10036                     this.fxanim({
10037                         height:{to:1},
10038                         points:{by:[0, this.getHeight() * .5]}
10039                     }, o, 'motion', 0.3, 'easeIn', after);
10040                 }).defer(100, this);
10041             });
10042         });
10043         return this;
10044     },
10045
10046     /**
10047      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10048      * changed using the "attr" config option) and then fading back to the original color. If no original
10049      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10050      * Usage:
10051 <pre><code>
10052 // default: highlight background to yellow
10053 el.highlight();
10054
10055 // custom: highlight foreground text to blue for 2 seconds
10056 el.highlight("0000ff", { attr: 'color', duration: 2 });
10057
10058 // common config options shown with default values
10059 el.highlight("ffff9c", {
10060     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10061     endColor: (current color) or "ffffff",
10062     easing: 'easeIn',
10063     duration: 1
10064 });
10065 </code></pre>
10066      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10067      * @param {Object} options (optional) Object literal with any of the Fx config options
10068      * @return {Roo.Element} The Element
10069      */ 
10070     highlight : function(color, o){
10071         var el = this.getFxEl();
10072         o = o || {};
10073
10074         el.queueFx(o, function(){
10075             color = color || "ffff9c";
10076             attr = o.attr || "backgroundColor";
10077
10078             this.clearOpacity();
10079             this.show();
10080
10081             var origColor = this.getColor(attr);
10082             var restoreColor = this.dom.style[attr];
10083             endColor = (o.endColor || origColor) || "ffffff";
10084
10085             var after = function(){
10086                 el.dom.style[attr] = restoreColor;
10087                 el.afterFx(o);
10088             };
10089
10090             var a = {};
10091             a[attr] = {from: color, to: endColor};
10092             arguments.callee.anim = this.fxanim(a,
10093                 o,
10094                 'color',
10095                 1,
10096                 'easeIn', after);
10097         });
10098         return this;
10099     },
10100
10101    /**
10102     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10103     * Usage:
10104 <pre><code>
10105 // default: a single light blue ripple
10106 el.frame();
10107
10108 // custom: 3 red ripples lasting 3 seconds total
10109 el.frame("ff0000", 3, { duration: 3 });
10110
10111 // common config options shown with default values
10112 el.frame("C3DAF9", 1, {
10113     duration: 1 //duration of entire animation (not each individual ripple)
10114     // Note: Easing is not configurable and will be ignored if included
10115 });
10116 </code></pre>
10117     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10118     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10119     * @param {Object} options (optional) Object literal with any of the Fx config options
10120     * @return {Roo.Element} The Element
10121     */
10122     frame : function(color, count, o){
10123         var el = this.getFxEl();
10124         o = o || {};
10125
10126         el.queueFx(o, function(){
10127             color = color || "#C3DAF9";
10128             if(color.length == 6){
10129                 color = "#" + color;
10130             }
10131             count = count || 1;
10132             duration = o.duration || 1;
10133             this.show();
10134
10135             var b = this.getBox();
10136             var animFn = function(){
10137                 var proxy = this.createProxy({
10138
10139                      style:{
10140                         visbility:"hidden",
10141                         position:"absolute",
10142                         "z-index":"35000", // yee haw
10143                         border:"0px solid " + color
10144                      }
10145                   });
10146                 var scale = Roo.isBorderBox ? 2 : 1;
10147                 proxy.animate({
10148                     top:{from:b.y, to:b.y - 20},
10149                     left:{from:b.x, to:b.x - 20},
10150                     borderWidth:{from:0, to:10},
10151                     opacity:{from:1, to:0},
10152                     height:{from:b.height, to:(b.height + (20*scale))},
10153                     width:{from:b.width, to:(b.width + (20*scale))}
10154                 }, duration, function(){
10155                     proxy.remove();
10156                 });
10157                 if(--count > 0){
10158                      animFn.defer((duration/2)*1000, this);
10159                 }else{
10160                     el.afterFx(o);
10161                 }
10162             };
10163             animFn.call(this);
10164         });
10165         return this;
10166     },
10167
10168    /**
10169     * Creates a pause before any subsequent queued effects begin.  If there are
10170     * no effects queued after the pause it will have no effect.
10171     * Usage:
10172 <pre><code>
10173 el.pause(1);
10174 </code></pre>
10175     * @param {Number} seconds The length of time to pause (in seconds)
10176     * @return {Roo.Element} The Element
10177     */
10178     pause : function(seconds){
10179         var el = this.getFxEl();
10180         var o = {};
10181
10182         el.queueFx(o, function(){
10183             setTimeout(function(){
10184                 el.afterFx(o);
10185             }, seconds * 1000);
10186         });
10187         return this;
10188     },
10189
10190    /**
10191     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10192     * using the "endOpacity" config option.
10193     * Usage:
10194 <pre><code>
10195 // default: fade in from opacity 0 to 100%
10196 el.fadeIn();
10197
10198 // custom: fade in from opacity 0 to 75% over 2 seconds
10199 el.fadeIn({ endOpacity: .75, duration: 2});
10200
10201 // common config options shown with default values
10202 el.fadeIn({
10203     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10204     easing: 'easeOut',
10205     duration: .5
10206 });
10207 </code></pre>
10208     * @param {Object} options (optional) Object literal with any of the Fx config options
10209     * @return {Roo.Element} The Element
10210     */
10211     fadeIn : function(o){
10212         var el = this.getFxEl();
10213         o = o || {};
10214         el.queueFx(o, function(){
10215             this.setOpacity(0);
10216             this.fixDisplay();
10217             this.dom.style.visibility = 'visible';
10218             var to = o.endOpacity || 1;
10219             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10220                 o, null, .5, "easeOut", function(){
10221                 if(to == 1){
10222                     this.clearOpacity();
10223                 }
10224                 el.afterFx(o);
10225             });
10226         });
10227         return this;
10228     },
10229
10230    /**
10231     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10232     * using the "endOpacity" config option.
10233     * Usage:
10234 <pre><code>
10235 // default: fade out from the element's current opacity to 0
10236 el.fadeOut();
10237
10238 // custom: fade out from the element's current opacity to 25% over 2 seconds
10239 el.fadeOut({ endOpacity: .25, duration: 2});
10240
10241 // common config options shown with default values
10242 el.fadeOut({
10243     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10244     easing: 'easeOut',
10245     duration: .5
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250     * @param {Object} options (optional) Object literal with any of the Fx config options
10251     * @return {Roo.Element} The Element
10252     */
10253     fadeOut : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256         el.queueFx(o, function(){
10257             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10258                 o, null, .5, "easeOut", function(){
10259                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10260                      this.dom.style.display = "none";
10261                 }else{
10262                      this.dom.style.visibility = "hidden";
10263                 }
10264                 this.clearOpacity();
10265                 el.afterFx(o);
10266             });
10267         });
10268         return this;
10269     },
10270
10271    /**
10272     * Animates the transition of an element's dimensions from a starting height/width
10273     * to an ending height/width.
10274     * Usage:
10275 <pre><code>
10276 // change height and width to 100x100 pixels
10277 el.scale(100, 100);
10278
10279 // common config options shown with default values.  The height and width will default to
10280 // the element's existing values if passed as null.
10281 el.scale(
10282     [element's width],
10283     [element's height], {
10284     easing: 'easeOut',
10285     duration: .35
10286 });
10287 </code></pre>
10288     * @param {Number} width  The new width (pass undefined to keep the original width)
10289     * @param {Number} height  The new height (pass undefined to keep the original height)
10290     * @param {Object} options (optional) Object literal with any of the Fx config options
10291     * @return {Roo.Element} The Element
10292     */
10293     scale : function(w, h, o){
10294         this.shift(Roo.apply({}, o, {
10295             width: w,
10296             height: h
10297         }));
10298         return this;
10299     },
10300
10301    /**
10302     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10303     * Any of these properties not specified in the config object will not be changed.  This effect 
10304     * requires that at least one new dimension, position or opacity setting must be passed in on
10305     * the config object in order for the function to have any effect.
10306     * Usage:
10307 <pre><code>
10308 // slide the element horizontally to x position 200 while changing the height and opacity
10309 el.shift({ x: 200, height: 50, opacity: .8 });
10310
10311 // common config options shown with default values.
10312 el.shift({
10313     width: [element's width],
10314     height: [element's height],
10315     x: [element's x position],
10316     y: [element's y position],
10317     opacity: [element's opacity],
10318     easing: 'easeOut',
10319     duration: .35
10320 });
10321 </code></pre>
10322     * @param {Object} options  Object literal with any of the Fx config options
10323     * @return {Roo.Element} The Element
10324     */
10325     shift : function(o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328         el.queueFx(o, function(){
10329             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10330             if(w !== undefined){
10331                 a.width = {to: this.adjustWidth(w)};
10332             }
10333             if(h !== undefined){
10334                 a.height = {to: this.adjustHeight(h)};
10335             }
10336             if(x !== undefined || y !== undefined){
10337                 a.points = {to: [
10338                     x !== undefined ? x : this.getX(),
10339                     y !== undefined ? y : this.getY()
10340                 ]};
10341             }
10342             if(op !== undefined){
10343                 a.opacity = {to: op};
10344             }
10345             if(o.xy !== undefined){
10346                 a.points = {to: o.xy};
10347             }
10348             arguments.callee.anim = this.fxanim(a,
10349                 o, 'motion', .35, "easeOut", function(){
10350                 el.afterFx(o);
10351             });
10352         });
10353         return this;
10354     },
10355
10356         /**
10357          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10358          * ending point of the effect.
10359          * Usage:
10360          *<pre><code>
10361 // default: slide the element downward while fading out
10362 el.ghost();
10363
10364 // custom: slide the element out to the right with a 2-second duration
10365 el.ghost('r', { duration: 2 });
10366
10367 // common config options shown with default values
10368 el.ghost('b', {
10369     easing: 'easeOut',
10370     duration: .5
10371     remove: false,
10372     useDisplay: false
10373 });
10374 </code></pre>
10375          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10376          * @param {Object} options (optional) Object literal with any of the Fx config options
10377          * @return {Roo.Element} The Element
10378          */
10379     ghost : function(anchor, o){
10380         var el = this.getFxEl();
10381         o = o || {};
10382
10383         el.queueFx(o, function(){
10384             anchor = anchor || "b";
10385
10386             // restore values after effect
10387             var r = this.getFxRestore();
10388             var w = this.getWidth(),
10389                 h = this.getHeight();
10390
10391             var st = this.dom.style;
10392
10393             var after = function(){
10394                 if(o.useDisplay){
10395                     el.setDisplayed(false);
10396                 }else{
10397                     el.hide();
10398                 }
10399
10400                 el.clearOpacity();
10401                 el.setPositioning(r.pos);
10402                 st.width = r.width;
10403                 st.height = r.height;
10404
10405                 el.afterFx(o);
10406             };
10407
10408             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10409             switch(anchor.toLowerCase()){
10410                 case "t":
10411                     pt.by = [0, -h];
10412                 break;
10413                 case "l":
10414                     pt.by = [-w, 0];
10415                 break;
10416                 case "r":
10417                     pt.by = [w, 0];
10418                 break;
10419                 case "b":
10420                     pt.by = [0, h];
10421                 break;
10422                 case "tl":
10423                     pt.by = [-w, -h];
10424                 break;
10425                 case "bl":
10426                     pt.by = [-w, h];
10427                 break;
10428                 case "br":
10429                     pt.by = [w, h];
10430                 break;
10431                 case "tr":
10432                     pt.by = [w, -h];
10433                 break;
10434             }
10435
10436             arguments.callee.anim = this.fxanim(a,
10437                 o,
10438                 'motion',
10439                 .5,
10440                 "easeOut", after);
10441         });
10442         return this;
10443     },
10444
10445         /**
10446          * Ensures that all effects queued after syncFx is called on the element are
10447          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10448          * @return {Roo.Element} The Element
10449          */
10450     syncFx : function(){
10451         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10452             block : false,
10453             concurrent : true,
10454             stopFx : false
10455         });
10456         return this;
10457     },
10458
10459         /**
10460          * Ensures that all effects queued after sequenceFx is called on the element are
10461          * run in sequence.  This is the opposite of {@link #syncFx}.
10462          * @return {Roo.Element} The Element
10463          */
10464     sequenceFx : function(){
10465         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10466             block : false,
10467             concurrent : false,
10468             stopFx : false
10469         });
10470         return this;
10471     },
10472
10473         /* @private */
10474     nextFx : function(){
10475         var ef = this.fxQueue[0];
10476         if(ef){
10477             ef.call(this);
10478         }
10479     },
10480
10481         /**
10482          * Returns true if the element has any effects actively running or queued, else returns false.
10483          * @return {Boolean} True if element has active effects, else false
10484          */
10485     hasActiveFx : function(){
10486         return this.fxQueue && this.fxQueue[0];
10487     },
10488
10489         /**
10490          * Stops any running effects and clears the element's internal effects queue if it contains
10491          * any additional effects that haven't started yet.
10492          * @return {Roo.Element} The Element
10493          */
10494     stopFx : function(){
10495         if(this.hasActiveFx()){
10496             var cur = this.fxQueue[0];
10497             if(cur && cur.anim && cur.anim.isAnimated()){
10498                 this.fxQueue = [cur]; // clear out others
10499                 cur.anim.stop(true);
10500             }
10501         }
10502         return this;
10503     },
10504
10505         /* @private */
10506     beforeFx : function(o){
10507         if(this.hasActiveFx() && !o.concurrent){
10508            if(o.stopFx){
10509                this.stopFx();
10510                return true;
10511            }
10512            return false;
10513         }
10514         return true;
10515     },
10516
10517         /**
10518          * Returns true if the element is currently blocking so that no other effect can be queued
10519          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10520          * used to ensure that an effect initiated by a user action runs to completion prior to the
10521          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10522          * @return {Boolean} True if blocking, else false
10523          */
10524     hasFxBlock : function(){
10525         var q = this.fxQueue;
10526         return q && q[0] && q[0].block;
10527     },
10528
10529         /* @private */
10530     queueFx : function(o, fn){
10531         if(!this.fxQueue){
10532             this.fxQueue = [];
10533         }
10534         if(!this.hasFxBlock()){
10535             Roo.applyIf(o, this.fxDefaults);
10536             if(!o.concurrent){
10537                 var run = this.beforeFx(o);
10538                 fn.block = o.block;
10539                 this.fxQueue.push(fn);
10540                 if(run){
10541                     this.nextFx();
10542                 }
10543             }else{
10544                 fn.call(this);
10545             }
10546         }
10547         return this;
10548     },
10549
10550         /* @private */
10551     fxWrap : function(pos, o, vis){
10552         var wrap;
10553         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10554             var wrapXY;
10555             if(o.fixPosition){
10556                 wrapXY = this.getXY();
10557             }
10558             var div = document.createElement("div");
10559             div.style.visibility = vis;
10560             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10561             wrap.setPositioning(pos);
10562             if(wrap.getStyle("position") == "static"){
10563                 wrap.position("relative");
10564             }
10565             this.clearPositioning('auto');
10566             wrap.clip();
10567             wrap.dom.appendChild(this.dom);
10568             if(wrapXY){
10569                 wrap.setXY(wrapXY);
10570             }
10571         }
10572         return wrap;
10573     },
10574
10575         /* @private */
10576     fxUnwrap : function(wrap, pos, o){
10577         this.clearPositioning();
10578         this.setPositioning(pos);
10579         if(!o.wrap){
10580             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10581             wrap.remove();
10582         }
10583     },
10584
10585         /* @private */
10586     getFxRestore : function(){
10587         var st = this.dom.style;
10588         return {pos: this.getPositioning(), width: st.width, height : st.height};
10589     },
10590
10591         /* @private */
10592     afterFx : function(o){
10593         if(o.afterStyle){
10594             this.applyStyles(o.afterStyle);
10595         }
10596         if(o.afterCls){
10597             this.addClass(o.afterCls);
10598         }
10599         if(o.remove === true){
10600             this.remove();
10601         }
10602         Roo.callback(o.callback, o.scope, [this]);
10603         if(!o.concurrent){
10604             this.fxQueue.shift();
10605             this.nextFx();
10606         }
10607     },
10608
10609         /* @private */
10610     getFxEl : function(){ // support for composite element fx
10611         return Roo.get(this.dom);
10612     },
10613
10614         /* @private */
10615     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10616         animType = animType || 'run';
10617         opt = opt || {};
10618         var anim = Roo.lib.Anim[animType](
10619             this.dom, args,
10620             (opt.duration || defaultDur) || .35,
10621             (opt.easing || defaultEase) || 'easeOut',
10622             function(){
10623                 Roo.callback(cb, this);
10624             },
10625             this
10626         );
10627         opt.anim = anim;
10628         return anim;
10629     }
10630 };
10631
10632 // backwords compat
10633 Roo.Fx.resize = Roo.Fx.scale;
10634
10635 //When included, Roo.Fx is automatically applied to Element so that all basic
10636 //effects are available directly via the Element API
10637 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10638  * Based on:
10639  * Ext JS Library 1.1.1
10640  * Copyright(c) 2006-2007, Ext JS, LLC.
10641  *
10642  * Originally Released Under LGPL - original licence link has changed is not relivant.
10643  *
10644  * Fork - LGPL
10645  * <script type="text/javascript">
10646  */
10647
10648
10649 /**
10650  * @class Roo.CompositeElement
10651  * Standard composite class. Creates a Roo.Element for every element in the collection.
10652  * <br><br>
10653  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10654  * actions will be performed on all the elements in this collection.</b>
10655  * <br><br>
10656  * All methods return <i>this</i> and can be chained.
10657  <pre><code>
10658  var els = Roo.select("#some-el div.some-class", true);
10659  // or select directly from an existing element
10660  var el = Roo.get('some-el');
10661  el.select('div.some-class', true);
10662
10663  els.setWidth(100); // all elements become 100 width
10664  els.hide(true); // all elements fade out and hide
10665  // or
10666  els.setWidth(100).hide(true);
10667  </code></pre>
10668  */
10669 Roo.CompositeElement = function(els){
10670     this.elements = [];
10671     this.addElements(els);
10672 };
10673 Roo.CompositeElement.prototype = {
10674     isComposite: true,
10675     addElements : function(els){
10676         if(!els) return this;
10677         if(typeof els == "string"){
10678             els = Roo.Element.selectorFunction(els);
10679         }
10680         var yels = this.elements;
10681         var index = yels.length-1;
10682         for(var i = 0, len = els.length; i < len; i++) {
10683                 yels[++index] = Roo.get(els[i]);
10684         }
10685         return this;
10686     },
10687
10688     /**
10689     * Clears this composite and adds the elements returned by the passed selector.
10690     * @param {String/Array} els A string CSS selector, an array of elements or an element
10691     * @return {CompositeElement} this
10692     */
10693     fill : function(els){
10694         this.elements = [];
10695         this.add(els);
10696         return this;
10697     },
10698
10699     /**
10700     * Filters this composite to only elements that match the passed selector.
10701     * @param {String} selector A string CSS selector
10702     * @return {CompositeElement} this
10703     */
10704     filter : function(selector){
10705         var els = [];
10706         this.each(function(el){
10707             if(el.is(selector)){
10708                 els[els.length] = el.dom;
10709             }
10710         });
10711         this.fill(els);
10712         return this;
10713     },
10714
10715     invoke : function(fn, args){
10716         var els = this.elements;
10717         for(var i = 0, len = els.length; i < len; i++) {
10718                 Roo.Element.prototype[fn].apply(els[i], args);
10719         }
10720         return this;
10721     },
10722     /**
10723     * Adds elements to this composite.
10724     * @param {String/Array} els A string CSS selector, an array of elements or an element
10725     * @return {CompositeElement} this
10726     */
10727     add : function(els){
10728         if(typeof els == "string"){
10729             this.addElements(Roo.Element.selectorFunction(els));
10730         }else if(els.length !== undefined){
10731             this.addElements(els);
10732         }else{
10733             this.addElements([els]);
10734         }
10735         return this;
10736     },
10737     /**
10738     * Calls the passed function passing (el, this, index) for each element in this composite.
10739     * @param {Function} fn The function to call
10740     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10741     * @return {CompositeElement} this
10742     */
10743     each : function(fn, scope){
10744         var els = this.elements;
10745         for(var i = 0, len = els.length; i < len; i++){
10746             if(fn.call(scope || els[i], els[i], this, i) === false) {
10747                 break;
10748             }
10749         }
10750         return this;
10751     },
10752
10753     /**
10754      * Returns the Element object at the specified index
10755      * @param {Number} index
10756      * @return {Roo.Element}
10757      */
10758     item : function(index){
10759         return this.elements[index] || null;
10760     },
10761
10762     /**
10763      * Returns the first Element
10764      * @return {Roo.Element}
10765      */
10766     first : function(){
10767         return this.item(0);
10768     },
10769
10770     /**
10771      * Returns the last Element
10772      * @return {Roo.Element}
10773      */
10774     last : function(){
10775         return this.item(this.elements.length-1);
10776     },
10777
10778     /**
10779      * Returns the number of elements in this composite
10780      * @return Number
10781      */
10782     getCount : function(){
10783         return this.elements.length;
10784     },
10785
10786     /**
10787      * Returns true if this composite contains the passed element
10788      * @return Boolean
10789      */
10790     contains : function(el){
10791         return this.indexOf(el) !== -1;
10792     },
10793
10794     /**
10795      * Returns true if this composite contains the passed element
10796      * @return Boolean
10797      */
10798     indexOf : function(el){
10799         return this.elements.indexOf(Roo.get(el));
10800     },
10801
10802
10803     /**
10804     * Removes the specified element(s).
10805     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10806     * or an array of any of those.
10807     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10808     * @return {CompositeElement} this
10809     */
10810     removeElement : function(el, removeDom){
10811         if(el instanceof Array){
10812             for(var i = 0, len = el.length; i < len; i++){
10813                 this.removeElement(el[i]);
10814             }
10815             return this;
10816         }
10817         var index = typeof el == 'number' ? el : this.indexOf(el);
10818         if(index !== -1){
10819             if(removeDom){
10820                 var d = this.elements[index];
10821                 if(d.dom){
10822                     d.remove();
10823                 }else{
10824                     d.parentNode.removeChild(d);
10825                 }
10826             }
10827             this.elements.splice(index, 1);
10828         }
10829         return this;
10830     },
10831
10832     /**
10833     * Replaces the specified element with the passed element.
10834     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10835     * to replace.
10836     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10837     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10838     * @return {CompositeElement} this
10839     */
10840     replaceElement : function(el, replacement, domReplace){
10841         var index = typeof el == 'number' ? el : this.indexOf(el);
10842         if(index !== -1){
10843             if(domReplace){
10844                 this.elements[index].replaceWith(replacement);
10845             }else{
10846                 this.elements.splice(index, 1, Roo.get(replacement))
10847             }
10848         }
10849         return this;
10850     },
10851
10852     /**
10853      * Removes all elements.
10854      */
10855     clear : function(){
10856         this.elements = [];
10857     }
10858 };
10859 (function(){
10860     Roo.CompositeElement.createCall = function(proto, fnName){
10861         if(!proto[fnName]){
10862             proto[fnName] = function(){
10863                 return this.invoke(fnName, arguments);
10864             };
10865         }
10866     };
10867     for(var fnName in Roo.Element.prototype){
10868         if(typeof Roo.Element.prototype[fnName] == "function"){
10869             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10870         }
10871     };
10872 })();
10873 /*
10874  * Based on:
10875  * Ext JS Library 1.1.1
10876  * Copyright(c) 2006-2007, Ext JS, LLC.
10877  *
10878  * Originally Released Under LGPL - original licence link has changed is not relivant.
10879  *
10880  * Fork - LGPL
10881  * <script type="text/javascript">
10882  */
10883
10884 /**
10885  * @class Roo.CompositeElementLite
10886  * @extends Roo.CompositeElement
10887  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10888  <pre><code>
10889  var els = Roo.select("#some-el div.some-class");
10890  // or select directly from an existing element
10891  var el = Roo.get('some-el');
10892  el.select('div.some-class');
10893
10894  els.setWidth(100); // all elements become 100 width
10895  els.hide(true); // all elements fade out and hide
10896  // or
10897  els.setWidth(100).hide(true);
10898  </code></pre><br><br>
10899  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10900  * actions will be performed on all the elements in this collection.</b>
10901  */
10902 Roo.CompositeElementLite = function(els){
10903     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10904     this.el = new Roo.Element.Flyweight();
10905 };
10906 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10907     addElements : function(els){
10908         if(els){
10909             if(els instanceof Array){
10910                 this.elements = this.elements.concat(els);
10911             }else{
10912                 var yels = this.elements;
10913                 var index = yels.length-1;
10914                 for(var i = 0, len = els.length; i < len; i++) {
10915                     yels[++index] = els[i];
10916                 }
10917             }
10918         }
10919         return this;
10920     },
10921     invoke : function(fn, args){
10922         var els = this.elements;
10923         var el = this.el;
10924         for(var i = 0, len = els.length; i < len; i++) {
10925             el.dom = els[i];
10926                 Roo.Element.prototype[fn].apply(el, args);
10927         }
10928         return this;
10929     },
10930     /**
10931      * Returns a flyweight Element of the dom element object at the specified index
10932      * @param {Number} index
10933      * @return {Roo.Element}
10934      */
10935     item : function(index){
10936         if(!this.elements[index]){
10937             return null;
10938         }
10939         this.el.dom = this.elements[index];
10940         return this.el;
10941     },
10942
10943     // fixes scope with flyweight
10944     addListener : function(eventName, handler, scope, opt){
10945         var els = this.elements;
10946         for(var i = 0, len = els.length; i < len; i++) {
10947             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10948         }
10949         return this;
10950     },
10951
10952     /**
10953     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10954     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10955     * a reference to the dom node, use el.dom.</b>
10956     * @param {Function} fn The function to call
10957     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10958     * @return {CompositeElement} this
10959     */
10960     each : function(fn, scope){
10961         var els = this.elements;
10962         var el = this.el;
10963         for(var i = 0, len = els.length; i < len; i++){
10964             el.dom = els[i];
10965                 if(fn.call(scope || el, el, this, i) === false){
10966                 break;
10967             }
10968         }
10969         return this;
10970     },
10971
10972     indexOf : function(el){
10973         return this.elements.indexOf(Roo.getDom(el));
10974     },
10975
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             replacement = Roo.getDom(replacement);
10980             if(domReplace){
10981                 var d = this.elements[index];
10982                 d.parentNode.insertBefore(replacement, d);
10983                 d.parentNode.removeChild(d);
10984             }
10985             this.elements.splice(index, 1, replacement);
10986         }
10987         return this;
10988     }
10989 });
10990 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10991
10992 /*
10993  * Based on:
10994  * Ext JS Library 1.1.1
10995  * Copyright(c) 2006-2007, Ext JS, LLC.
10996  *
10997  * Originally Released Under LGPL - original licence link has changed is not relivant.
10998  *
10999  * Fork - LGPL
11000  * <script type="text/javascript">
11001  */
11002
11003  
11004
11005 /**
11006  * @class Roo.data.Connection
11007  * @extends Roo.util.Observable
11008  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11009  * either to a configured URL, or to a URL specified at request time.<br><br>
11010  * <p>
11011  * Requests made by this class are asynchronous, and will return immediately. No data from
11012  * the server will be available to the statement immediately following the {@link #request} call.
11013  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11014  * <p>
11015  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11016  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11017  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11018  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11019  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11020  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11021  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11022  * standard DOM methods.
11023  * @constructor
11024  * @param {Object} config a configuration object.
11025  */
11026 Roo.data.Connection = function(config){
11027     Roo.apply(this, config);
11028     this.addEvents({
11029         /**
11030          * @event beforerequest
11031          * Fires before a network request is made to retrieve a data object.
11032          * @param {Connection} conn This Connection object.
11033          * @param {Object} options The options config object passed to the {@link #request} method.
11034          */
11035         "beforerequest" : true,
11036         /**
11037          * @event requestcomplete
11038          * Fires if the request was successfully completed.
11039          * @param {Connection} conn This Connection object.
11040          * @param {Object} response The XHR object containing the response data.
11041          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11042          * @param {Object} options The options config object passed to the {@link #request} method.
11043          */
11044         "requestcomplete" : true,
11045         /**
11046          * @event requestexception
11047          * Fires if an error HTTP status was returned from the server.
11048          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11049          * @param {Connection} conn This Connection object.
11050          * @param {Object} response The XHR object containing the response data.
11051          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11052          * @param {Object} options The options config object passed to the {@link #request} method.
11053          */
11054         "requestexception" : true
11055     });
11056     Roo.data.Connection.superclass.constructor.call(this);
11057 };
11058
11059 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11060     /**
11061      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11062      */
11063     /**
11064      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11065      * extra parameters to each request made by this object. (defaults to undefined)
11066      */
11067     /**
11068      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11069      *  to each request made by this object. (defaults to undefined)
11070      */
11071     /**
11072      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11073      */
11074     /**
11075      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11076      */
11077     timeout : 30000,
11078     /**
11079      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11080      * @type Boolean
11081      */
11082     autoAbort:false,
11083
11084     /**
11085      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11086      * @type Boolean
11087      */
11088     disableCaching: true,
11089
11090     /**
11091      * Sends an HTTP request to a remote server.
11092      * @param {Object} options An object which may contain the following properties:<ul>
11093      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11094      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11095      * request, a url encoded string or a function to call to get either.</li>
11096      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11097      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11098      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11099      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11100      * <li>options {Object} The parameter to the request call.</li>
11101      * <li>success {Boolean} True if the request succeeded.</li>
11102      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11103      * </ul></li>
11104      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11105      * The callback is passed the following parameters:<ul>
11106      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11107      * <li>options {Object} The parameter to the request call.</li>
11108      * </ul></li>
11109      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11110      * The callback is passed the following parameters:<ul>
11111      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11112      * <li>options {Object} The parameter to the request call.</li>
11113      * </ul></li>
11114      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11115      * for the callback function. Defaults to the browser window.</li>
11116      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11117      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11118      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11119      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11120      * params for the post data. Any params will be appended to the URL.</li>
11121      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11122      * </ul>
11123      * @return {Number} transactionId
11124      */
11125     request : function(o){
11126         if(this.fireEvent("beforerequest", this, o) !== false){
11127             var p = o.params;
11128
11129             if(typeof p == "function"){
11130                 p = p.call(o.scope||window, o);
11131             }
11132             if(typeof p == "object"){
11133                 p = Roo.urlEncode(o.params);
11134             }
11135             if(this.extraParams){
11136                 var extras = Roo.urlEncode(this.extraParams);
11137                 p = p ? (p + '&' + extras) : extras;
11138             }
11139
11140             var url = o.url || this.url;
11141             if(typeof url == 'function'){
11142                 url = url.call(o.scope||window, o);
11143             }
11144
11145             if(o.form){
11146                 var form = Roo.getDom(o.form);
11147                 url = url || form.action;
11148
11149                 var enctype = form.getAttribute("enctype");
11150                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11151                     return this.doFormUpload(o, p, url);
11152                 }
11153                 var f = Roo.lib.Ajax.serializeForm(form);
11154                 p = p ? (p + '&' + f) : f;
11155             }
11156
11157             var hs = o.headers;
11158             if(this.defaultHeaders){
11159                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11160                 if(!o.headers){
11161                     o.headers = hs;
11162                 }
11163             }
11164
11165             var cb = {
11166                 success: this.handleResponse,
11167                 failure: this.handleFailure,
11168                 scope: this,
11169                 argument: {options: o},
11170                 timeout : this.timeout
11171             };
11172
11173             var method = o.method||this.method||(p ? "POST" : "GET");
11174
11175             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11176                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11177             }
11178
11179             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11180                 if(o.autoAbort){
11181                     this.abort();
11182                 }
11183             }else if(this.autoAbort !== false){
11184                 this.abort();
11185             }
11186
11187             if((method == 'GET' && p) || o.xmlData){
11188                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11189                 p = '';
11190             }
11191             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11192             return this.transId;
11193         }else{
11194             Roo.callback(o.callback, o.scope, [o, null, null]);
11195             return null;
11196         }
11197     },
11198
11199     /**
11200      * Determine whether this object has a request outstanding.
11201      * @param {Number} transactionId (Optional) defaults to the last transaction
11202      * @return {Boolean} True if there is an outstanding request.
11203      */
11204     isLoading : function(transId){
11205         if(transId){
11206             return Roo.lib.Ajax.isCallInProgress(transId);
11207         }else{
11208             return this.transId ? true : false;
11209         }
11210     },
11211
11212     /**
11213      * Aborts any outstanding request.
11214      * @param {Number} transactionId (Optional) defaults to the last transaction
11215      */
11216     abort : function(transId){
11217         if(transId || this.isLoading()){
11218             Roo.lib.Ajax.abort(transId || this.transId);
11219         }
11220     },
11221
11222     // private
11223     handleResponse : function(response){
11224         this.transId = false;
11225         var options = response.argument.options;
11226         response.argument = options ? options.argument : null;
11227         this.fireEvent("requestcomplete", this, response, options);
11228         Roo.callback(options.success, options.scope, [response, options]);
11229         Roo.callback(options.callback, options.scope, [options, true, response]);
11230     },
11231
11232     // private
11233     handleFailure : function(response, e){
11234         this.transId = false;
11235         var options = response.argument.options;
11236         response.argument = options ? options.argument : null;
11237         this.fireEvent("requestexception", this, response, options, e);
11238         Roo.callback(options.failure, options.scope, [response, options]);
11239         Roo.callback(options.callback, options.scope, [options, false, response]);
11240     },
11241
11242     // private
11243     doFormUpload : function(o, ps, url){
11244         var id = Roo.id();
11245         var frame = document.createElement('iframe');
11246         frame.id = id;
11247         frame.name = id;
11248         frame.className = 'x-hidden';
11249         if(Roo.isIE){
11250             frame.src = Roo.SSL_SECURE_URL;
11251         }
11252         document.body.appendChild(frame);
11253
11254         if(Roo.isIE){
11255            document.frames[id].name = id;
11256         }
11257
11258         var form = Roo.getDom(o.form);
11259         form.target = id;
11260         form.method = 'POST';
11261         form.enctype = form.encoding = 'multipart/form-data';
11262         if(url){
11263             form.action = url;
11264         }
11265
11266         var hiddens, hd;
11267         if(ps){ // add dynamic params
11268             hiddens = [];
11269             ps = Roo.urlDecode(ps, false);
11270             for(var k in ps){
11271                 if(ps.hasOwnProperty(k)){
11272                     hd = document.createElement('input');
11273                     hd.type = 'hidden';
11274                     hd.name = k;
11275                     hd.value = ps[k];
11276                     form.appendChild(hd);
11277                     hiddens.push(hd);
11278                 }
11279             }
11280         }
11281
11282         function cb(){
11283             var r = {  // bogus response object
11284                 responseText : '',
11285                 responseXML : null
11286             };
11287
11288             r.argument = o ? o.argument : null;
11289
11290             try { //
11291                 var doc;
11292                 if(Roo.isIE){
11293                     doc = frame.contentWindow.document;
11294                 }else {
11295                     doc = (frame.contentDocument || window.frames[id].document);
11296                 }
11297                 if(doc && doc.body){
11298                     r.responseText = doc.body.innerHTML;
11299                 }
11300                 if(doc && doc.XMLDocument){
11301                     r.responseXML = doc.XMLDocument;
11302                 }else {
11303                     r.responseXML = doc;
11304                 }
11305             }
11306             catch(e) {
11307                 // ignore
11308             }
11309
11310             Roo.EventManager.removeListener(frame, 'load', cb, this);
11311
11312             this.fireEvent("requestcomplete", this, r, o);
11313             Roo.callback(o.success, o.scope, [r, o]);
11314             Roo.callback(o.callback, o.scope, [o, true, r]);
11315
11316             setTimeout(function(){document.body.removeChild(frame);}, 100);
11317         }
11318
11319         Roo.EventManager.on(frame, 'load', cb, this);
11320         form.submit();
11321
11322         if(hiddens){ // remove dynamic params
11323             for(var i = 0, len = hiddens.length; i < len; i++){
11324                 form.removeChild(hiddens[i]);
11325             }
11326         }
11327     }
11328 });
11329
11330 /**
11331  * @class Roo.Ajax
11332  * @extends Roo.data.Connection
11333  * Global Ajax request class.
11334  *
11335  * @singleton
11336  */
11337 Roo.Ajax = new Roo.data.Connection({
11338     // fix up the docs
11339    /**
11340      * @cfg {String} url @hide
11341      */
11342     /**
11343      * @cfg {Object} extraParams @hide
11344      */
11345     /**
11346      * @cfg {Object} defaultHeaders @hide
11347      */
11348     /**
11349      * @cfg {String} method (Optional) @hide
11350      */
11351     /**
11352      * @cfg {Number} timeout (Optional) @hide
11353      */
11354     /**
11355      * @cfg {Boolean} autoAbort (Optional) @hide
11356      */
11357
11358     /**
11359      * @cfg {Boolean} disableCaching (Optional) @hide
11360      */
11361
11362     /**
11363      * @property  disableCaching
11364      * True to add a unique cache-buster param to GET requests. (defaults to true)
11365      * @type Boolean
11366      */
11367     /**
11368      * @property  url
11369      * The default URL to be used for requests to the server. (defaults to undefined)
11370      * @type String
11371      */
11372     /**
11373      * @property  extraParams
11374      * An object containing properties which are used as
11375      * extra parameters to each request made by this object. (defaults to undefined)
11376      * @type Object
11377      */
11378     /**
11379      * @property  defaultHeaders
11380      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11381      * @type Object
11382      */
11383     /**
11384      * @property  method
11385      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11386      * @type String
11387      */
11388     /**
11389      * @property  timeout
11390      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11391      * @type Number
11392      */
11393
11394     /**
11395      * @property  autoAbort
11396      * Whether a new request should abort any pending requests. (defaults to false)
11397      * @type Boolean
11398      */
11399     autoAbort : false,
11400
11401     /**
11402      * Serialize the passed form into a url encoded string
11403      * @param {String/HTMLElement} form
11404      * @return {String}
11405      */
11406     serializeForm : function(form){
11407         return Roo.lib.Ajax.serializeForm(form);
11408     }
11409 });/*
11410  * Based on:
11411  * Ext JS Library 1.1.1
11412  * Copyright(c) 2006-2007, Ext JS, LLC.
11413  *
11414  * Originally Released Under LGPL - original licence link has changed is not relivant.
11415  *
11416  * Fork - LGPL
11417  * <script type="text/javascript">
11418  */
11419  
11420 /**
11421  * @class Roo.Ajax
11422  * @extends Roo.data.Connection
11423  * Global Ajax request class.
11424  *
11425  * @instanceOf  Roo.data.Connection
11426  */
11427 Roo.Ajax = new Roo.data.Connection({
11428     // fix up the docs
11429     
11430     /**
11431      * fix up scoping
11432      * @scope Roo.Ajax
11433      */
11434     
11435    /**
11436      * @cfg {String} url @hide
11437      */
11438     /**
11439      * @cfg {Object} extraParams @hide
11440      */
11441     /**
11442      * @cfg {Object} defaultHeaders @hide
11443      */
11444     /**
11445      * @cfg {String} method (Optional) @hide
11446      */
11447     /**
11448      * @cfg {Number} timeout (Optional) @hide
11449      */
11450     /**
11451      * @cfg {Boolean} autoAbort (Optional) @hide
11452      */
11453
11454     /**
11455      * @cfg {Boolean} disableCaching (Optional) @hide
11456      */
11457
11458     /**
11459      * @property  disableCaching
11460      * True to add a unique cache-buster param to GET requests. (defaults to true)
11461      * @type Boolean
11462      */
11463     /**
11464      * @property  url
11465      * The default URL to be used for requests to the server. (defaults to undefined)
11466      * @type String
11467      */
11468     /**
11469      * @property  extraParams
11470      * An object containing properties which are used as
11471      * extra parameters to each request made by this object. (defaults to undefined)
11472      * @type Object
11473      */
11474     /**
11475      * @property  defaultHeaders
11476      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11477      * @type Object
11478      */
11479     /**
11480      * @property  method
11481      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11482      * @type String
11483      */
11484     /**
11485      * @property  timeout
11486      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11487      * @type Number
11488      */
11489
11490     /**
11491      * @property  autoAbort
11492      * Whether a new request should abort any pending requests. (defaults to false)
11493      * @type Boolean
11494      */
11495     autoAbort : false,
11496
11497     /**
11498      * Serialize the passed form into a url encoded string
11499      * @param {String/HTMLElement} form
11500      * @return {String}
11501      */
11502     serializeForm : function(form){
11503         return Roo.lib.Ajax.serializeForm(form);
11504     }
11505 });/*
11506  * Based on:
11507  * Ext JS Library 1.1.1
11508  * Copyright(c) 2006-2007, Ext JS, LLC.
11509  *
11510  * Originally Released Under LGPL - original licence link has changed is not relivant.
11511  *
11512  * Fork - LGPL
11513  * <script type="text/javascript">
11514  */
11515
11516  
11517 /**
11518  * @class Roo.UpdateManager
11519  * @extends Roo.util.Observable
11520  * Provides AJAX-style update for Element object.<br><br>
11521  * Usage:<br>
11522  * <pre><code>
11523  * // Get it from a Roo.Element object
11524  * var el = Roo.get("foo");
11525  * var mgr = el.getUpdateManager();
11526  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11527  * ...
11528  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11529  * <br>
11530  * // or directly (returns the same UpdateManager instance)
11531  * var mgr = new Roo.UpdateManager("myElementId");
11532  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11533  * mgr.on("update", myFcnNeedsToKnow);
11534  * <br>
11535    // short handed call directly from the element object
11536    Roo.get("foo").load({
11537         url: "bar.php",
11538         scripts:true,
11539         params: "for=bar",
11540         text: "Loading Foo..."
11541    });
11542  * </code></pre>
11543  * @constructor
11544  * Create new UpdateManager directly.
11545  * @param {String/HTMLElement/Roo.Element} el The element to update
11546  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11547  */
11548 Roo.UpdateManager = function(el, forceNew){
11549     el = Roo.get(el);
11550     if(!forceNew && el.updateManager){
11551         return el.updateManager;
11552     }
11553     /**
11554      * The Element object
11555      * @type Roo.Element
11556      */
11557     this.el = el;
11558     /**
11559      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11560      * @type String
11561      */
11562     this.defaultUrl = null;
11563
11564     this.addEvents({
11565         /**
11566          * @event beforeupdate
11567          * Fired before an update is made, return false from your handler and the update is cancelled.
11568          * @param {Roo.Element} el
11569          * @param {String/Object/Function} url
11570          * @param {String/Object} params
11571          */
11572         "beforeupdate": true,
11573         /**
11574          * @event update
11575          * Fired after successful update is made.
11576          * @param {Roo.Element} el
11577          * @param {Object} oResponseObject The response Object
11578          */
11579         "update": true,
11580         /**
11581          * @event failure
11582          * Fired on update failure.
11583          * @param {Roo.Element} el
11584          * @param {Object} oResponseObject The response Object
11585          */
11586         "failure": true
11587     });
11588     var d = Roo.UpdateManager.defaults;
11589     /**
11590      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11591      * @type String
11592      */
11593     this.sslBlankUrl = d.sslBlankUrl;
11594     /**
11595      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11596      * @type Boolean
11597      */
11598     this.disableCaching = d.disableCaching;
11599     /**
11600      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11601      * @type String
11602      */
11603     this.indicatorText = d.indicatorText;
11604     /**
11605      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11606      * @type String
11607      */
11608     this.showLoadIndicator = d.showLoadIndicator;
11609     /**
11610      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11611      * @type Number
11612      */
11613     this.timeout = d.timeout;
11614
11615     /**
11616      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11617      * @type Boolean
11618      */
11619     this.loadScripts = d.loadScripts;
11620
11621     /**
11622      * Transaction object of current executing transaction
11623      */
11624     this.transaction = null;
11625
11626     /**
11627      * @private
11628      */
11629     this.autoRefreshProcId = null;
11630     /**
11631      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11632      * @type Function
11633      */
11634     this.refreshDelegate = this.refresh.createDelegate(this);
11635     /**
11636      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11637      * @type Function
11638      */
11639     this.updateDelegate = this.update.createDelegate(this);
11640     /**
11641      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11642      * @type Function
11643      */
11644     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11645     /**
11646      * @private
11647      */
11648     this.successDelegate = this.processSuccess.createDelegate(this);
11649     /**
11650      * @private
11651      */
11652     this.failureDelegate = this.processFailure.createDelegate(this);
11653
11654     if(!this.renderer){
11655      /**
11656       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11657       */
11658     this.renderer = new Roo.UpdateManager.BasicRenderer();
11659     }
11660     
11661     Roo.UpdateManager.superclass.constructor.call(this);
11662 };
11663
11664 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11665     /**
11666      * Get the Element this UpdateManager is bound to
11667      * @return {Roo.Element} The element
11668      */
11669     getEl : function(){
11670         return this.el;
11671     },
11672     /**
11673      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11674      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11675 <pre><code>
11676 um.update({<br/>
11677     url: "your-url.php",<br/>
11678     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11679     callback: yourFunction,<br/>
11680     scope: yourObject, //(optional scope)  <br/>
11681     discardUrl: false, <br/>
11682     nocache: false,<br/>
11683     text: "Loading...",<br/>
11684     timeout: 30,<br/>
11685     scripts: false<br/>
11686 });
11687 </code></pre>
11688      * The only required property is url. The optional properties nocache, text and scripts
11689      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11690      * @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}
11691      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11692      * @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.
11693      */
11694     update : function(url, params, callback, discardUrl){
11695         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11696             var method = this.method, cfg;
11697             if(typeof url == "object"){ // must be config object
11698                 cfg = url;
11699                 url = cfg.url;
11700                 params = params || cfg.params;
11701                 callback = callback || cfg.callback;
11702                 discardUrl = discardUrl || cfg.discardUrl;
11703                 if(callback && cfg.scope){
11704                     callback = callback.createDelegate(cfg.scope);
11705                 }
11706                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11707                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11708                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11709                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11710                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11711             }
11712             this.showLoading();
11713             if(!discardUrl){
11714                 this.defaultUrl = url;
11715             }
11716             if(typeof url == "function"){
11717                 url = url.call(this);
11718             }
11719
11720             method = method || (params ? "POST" : "GET");
11721             if(method == "GET"){
11722                 url = this.prepareUrl(url);
11723             }
11724
11725             var o = Roo.apply(cfg ||{}, {
11726                 url : url,
11727                 params: params,
11728                 success: this.successDelegate,
11729                 failure: this.failureDelegate,
11730                 callback: undefined,
11731                 timeout: (this.timeout*1000),
11732                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11733             });
11734
11735             this.transaction = Roo.Ajax.request(o);
11736         }
11737     },
11738
11739     /**
11740      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11741      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11742      * @param {String/HTMLElement} form The form Id or form element
11743      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11744      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11745      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11746      */
11747     formUpdate : function(form, url, reset, callback){
11748         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11749             if(typeof url == "function"){
11750                 url = url.call(this);
11751             }
11752             form = Roo.getDom(form);
11753             this.transaction = Roo.Ajax.request({
11754                 form: form,
11755                 url:url,
11756                 success: this.successDelegate,
11757                 failure: this.failureDelegate,
11758                 timeout: (this.timeout*1000),
11759                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11760             });
11761             this.showLoading.defer(1, this);
11762         }
11763     },
11764
11765     /**
11766      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11767      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11768      */
11769     refresh : function(callback){
11770         if(this.defaultUrl == null){
11771             return;
11772         }
11773         this.update(this.defaultUrl, null, callback, true);
11774     },
11775
11776     /**
11777      * Set this element to auto refresh.
11778      * @param {Number} interval How often to update (in seconds).
11779      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11780      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11781      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11782      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11783      */
11784     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11785         if(refreshNow){
11786             this.update(url || this.defaultUrl, params, callback, true);
11787         }
11788         if(this.autoRefreshProcId){
11789             clearInterval(this.autoRefreshProcId);
11790         }
11791         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11792     },
11793
11794     /**
11795      * Stop auto refresh on this element.
11796      */
11797      stopAutoRefresh : function(){
11798         if(this.autoRefreshProcId){
11799             clearInterval(this.autoRefreshProcId);
11800             delete this.autoRefreshProcId;
11801         }
11802     },
11803
11804     isAutoRefreshing : function(){
11805        return this.autoRefreshProcId ? true : false;
11806     },
11807     /**
11808      * Called to update the element to "Loading" state. Override to perform custom action.
11809      */
11810     showLoading : function(){
11811         if(this.showLoadIndicator){
11812             this.el.update(this.indicatorText);
11813         }
11814     },
11815
11816     /**
11817      * Adds unique parameter to query string if disableCaching = true
11818      * @private
11819      */
11820     prepareUrl : function(url){
11821         if(this.disableCaching){
11822             var append = "_dc=" + (new Date().getTime());
11823             if(url.indexOf("?") !== -1){
11824                 url += "&" + append;
11825             }else{
11826                 url += "?" + append;
11827             }
11828         }
11829         return url;
11830     },
11831
11832     /**
11833      * @private
11834      */
11835     processSuccess : function(response){
11836         this.transaction = null;
11837         if(response.argument.form && response.argument.reset){
11838             try{ // put in try/catch since some older FF releases had problems with this
11839                 response.argument.form.reset();
11840             }catch(e){}
11841         }
11842         if(this.loadScripts){
11843             this.renderer.render(this.el, response, this,
11844                 this.updateComplete.createDelegate(this, [response]));
11845         }else{
11846             this.renderer.render(this.el, response, this);
11847             this.updateComplete(response);
11848         }
11849     },
11850
11851     updateComplete : function(response){
11852         this.fireEvent("update", this.el, response);
11853         if(typeof response.argument.callback == "function"){
11854             response.argument.callback(this.el, true, response);
11855         }
11856     },
11857
11858     /**
11859      * @private
11860      */
11861     processFailure : function(response){
11862         this.transaction = null;
11863         this.fireEvent("failure", this.el, response);
11864         if(typeof response.argument.callback == "function"){
11865             response.argument.callback(this.el, false, response);
11866         }
11867     },
11868
11869     /**
11870      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11871      * @param {Object} renderer The object implementing the render() method
11872      */
11873     setRenderer : function(renderer){
11874         this.renderer = renderer;
11875     },
11876
11877     getRenderer : function(){
11878        return this.renderer;
11879     },
11880
11881     /**
11882      * Set the defaultUrl used for updates
11883      * @param {String/Function} defaultUrl The url or a function to call to get the url
11884      */
11885     setDefaultUrl : function(defaultUrl){
11886         this.defaultUrl = defaultUrl;
11887     },
11888
11889     /**
11890      * Aborts the executing transaction
11891      */
11892     abort : function(){
11893         if(this.transaction){
11894             Roo.Ajax.abort(this.transaction);
11895         }
11896     },
11897
11898     /**
11899      * Returns true if an update is in progress
11900      * @return {Boolean}
11901      */
11902     isUpdating : function(){
11903         if(this.transaction){
11904             return Roo.Ajax.isLoading(this.transaction);
11905         }
11906         return false;
11907     }
11908 });
11909
11910 /**
11911  * @class Roo.UpdateManager.defaults
11912  * @static (not really - but it helps the doc tool)
11913  * The defaults collection enables customizing the default properties of UpdateManager
11914  */
11915    Roo.UpdateManager.defaults = {
11916        /**
11917          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11918          * @type Number
11919          */
11920          timeout : 30,
11921
11922          /**
11923          * True to process scripts by default (Defaults to false).
11924          * @type Boolean
11925          */
11926         loadScripts : false,
11927
11928         /**
11929         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11930         * @type String
11931         */
11932         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11933         /**
11934          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11935          * @type Boolean
11936          */
11937         disableCaching : false,
11938         /**
11939          * Whether to show indicatorText when loading (Defaults to true).
11940          * @type Boolean
11941          */
11942         showLoadIndicator : true,
11943         /**
11944          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11945          * @type String
11946          */
11947         indicatorText : '<div class="loading-indicator">Loading...</div>'
11948    };
11949
11950 /**
11951  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11952  *Usage:
11953  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11954  * @param {String/HTMLElement/Roo.Element} el The element to update
11955  * @param {String} url The url
11956  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11957  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11958  * @static
11959  * @deprecated
11960  * @member Roo.UpdateManager
11961  */
11962 Roo.UpdateManager.updateElement = function(el, url, params, options){
11963     var um = Roo.get(el, true).getUpdateManager();
11964     Roo.apply(um, options);
11965     um.update(url, params, options ? options.callback : null);
11966 };
11967 // alias for backwards compat
11968 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11969 /**
11970  * @class Roo.UpdateManager.BasicRenderer
11971  * Default Content renderer. Updates the elements innerHTML with the responseText.
11972  */
11973 Roo.UpdateManager.BasicRenderer = function(){};
11974
11975 Roo.UpdateManager.BasicRenderer.prototype = {
11976     /**
11977      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11978      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11979      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11980      * @param {Roo.Element} el The element being rendered
11981      * @param {Object} response The YUI Connect response object
11982      * @param {UpdateManager} updateManager The calling update manager
11983      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11984      */
11985      render : function(el, response, updateManager, callback){
11986         el.update(response.responseText, updateManager.loadScripts, callback);
11987     }
11988 };
11989 /*
11990  * Based on:
11991  * Ext JS Library 1.1.1
11992  * Copyright(c) 2006-2007, Ext JS, LLC.
11993  *
11994  * Originally Released Under LGPL - original licence link has changed is not relivant.
11995  *
11996  * Fork - LGPL
11997  * <script type="text/javascript">
11998  */
11999
12000 /**
12001  * @class Roo.util.DelayedTask
12002  * Provides a convenient method of performing setTimeout where a new
12003  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12004  * You can use this class to buffer
12005  * the keypress events for a certain number of milliseconds, and perform only if they stop
12006  * for that amount of time.
12007  * @constructor The parameters to this constructor serve as defaults and are not required.
12008  * @param {Function} fn (optional) The default function to timeout
12009  * @param {Object} scope (optional) The default scope of that timeout
12010  * @param {Array} args (optional) The default Array of arguments
12011  */
12012 Roo.util.DelayedTask = function(fn, scope, args){
12013     var id = null, d, t;
12014
12015     var call = function(){
12016         var now = new Date().getTime();
12017         if(now - t >= d){
12018             clearInterval(id);
12019             id = null;
12020             fn.apply(scope, args || []);
12021         }
12022     };
12023     /**
12024      * Cancels any pending timeout and queues a new one
12025      * @param {Number} delay The milliseconds to delay
12026      * @param {Function} newFn (optional) Overrides function passed to constructor
12027      * @param {Object} newScope (optional) Overrides scope passed to constructor
12028      * @param {Array} newArgs (optional) Overrides args passed to constructor
12029      */
12030     this.delay = function(delay, newFn, newScope, newArgs){
12031         if(id && delay != d){
12032             this.cancel();
12033         }
12034         d = delay;
12035         t = new Date().getTime();
12036         fn = newFn || fn;
12037         scope = newScope || scope;
12038         args = newArgs || args;
12039         if(!id){
12040             id = setInterval(call, d);
12041         }
12042     };
12043
12044     /**
12045      * Cancel the last queued timeout
12046      */
12047     this.cancel = function(){
12048         if(id){
12049             clearInterval(id);
12050             id = null;
12051         }
12052     };
12053 };/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064  
12065 Roo.util.TaskRunner = function(interval){
12066     interval = interval || 10;
12067     var tasks = [], removeQueue = [];
12068     var id = 0;
12069     var running = false;
12070
12071     var stopThread = function(){
12072         running = false;
12073         clearInterval(id);
12074         id = 0;
12075     };
12076
12077     var startThread = function(){
12078         if(!running){
12079             running = true;
12080             id = setInterval(runTasks, interval);
12081         }
12082     };
12083
12084     var removeTask = function(task){
12085         removeQueue.push(task);
12086         if(task.onStop){
12087             task.onStop();
12088         }
12089     };
12090
12091     var runTasks = function(){
12092         if(removeQueue.length > 0){
12093             for(var i = 0, len = removeQueue.length; i < len; i++){
12094                 tasks.remove(removeQueue[i]);
12095             }
12096             removeQueue = [];
12097             if(tasks.length < 1){
12098                 stopThread();
12099                 return;
12100             }
12101         }
12102         var now = new Date().getTime();
12103         for(var i = 0, len = tasks.length; i < len; ++i){
12104             var t = tasks[i];
12105             var itime = now - t.taskRunTime;
12106             if(t.interval <= itime){
12107                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12108                 t.taskRunTime = now;
12109                 if(rt === false || t.taskRunCount === t.repeat){
12110                     removeTask(t);
12111                     return;
12112                 }
12113             }
12114             if(t.duration && t.duration <= (now - t.taskStartTime)){
12115                 removeTask(t);
12116             }
12117         }
12118     };
12119
12120     /**
12121      * Queues a new task.
12122      * @param {Object} task
12123      */
12124     this.start = function(task){
12125         tasks.push(task);
12126         task.taskStartTime = new Date().getTime();
12127         task.taskRunTime = 0;
12128         task.taskRunCount = 0;
12129         startThread();
12130         return task;
12131     };
12132
12133     this.stop = function(task){
12134         removeTask(task);
12135         return task;
12136     };
12137
12138     this.stopAll = function(){
12139         stopThread();
12140         for(var i = 0, len = tasks.length; i < len; i++){
12141             if(tasks[i].onStop){
12142                 tasks[i].onStop();
12143             }
12144         }
12145         tasks = [];
12146         removeQueue = [];
12147     };
12148 };
12149
12150 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12151  * Based on:
12152  * Ext JS Library 1.1.1
12153  * Copyright(c) 2006-2007, Ext JS, LLC.
12154  *
12155  * Originally Released Under LGPL - original licence link has changed is not relivant.
12156  *
12157  * Fork - LGPL
12158  * <script type="text/javascript">
12159  */
12160
12161  
12162 /**
12163  * @class Roo.util.MixedCollection
12164  * @extends Roo.util.Observable
12165  * A Collection class that maintains both numeric indexes and keys and exposes events.
12166  * @constructor
12167  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12168  * collection (defaults to false)
12169  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12170  * and return the key value for that item.  This is used when available to look up the key on items that
12171  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12172  * equivalent to providing an implementation for the {@link #getKey} method.
12173  */
12174 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12175     this.items = [];
12176     this.map = {};
12177     this.keys = [];
12178     this.length = 0;
12179     this.addEvents({
12180         /**
12181          * @event clear
12182          * Fires when the collection is cleared.
12183          */
12184         "clear" : true,
12185         /**
12186          * @event add
12187          * Fires when an item is added to the collection.
12188          * @param {Number} index The index at which the item was added.
12189          * @param {Object} o The item added.
12190          * @param {String} key The key associated with the added item.
12191          */
12192         "add" : true,
12193         /**
12194          * @event replace
12195          * Fires when an item is replaced in the collection.
12196          * @param {String} key he key associated with the new added.
12197          * @param {Object} old The item being replaced.
12198          * @param {Object} new The new item.
12199          */
12200         "replace" : true,
12201         /**
12202          * @event remove
12203          * Fires when an item is removed from the collection.
12204          * @param {Object} o The item being removed.
12205          * @param {String} key (optional) The key associated with the removed item.
12206          */
12207         "remove" : true,
12208         "sort" : true
12209     });
12210     this.allowFunctions = allowFunctions === true;
12211     if(keyFn){
12212         this.getKey = keyFn;
12213     }
12214     Roo.util.MixedCollection.superclass.constructor.call(this);
12215 };
12216
12217 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12218     allowFunctions : false,
12219     
12220 /**
12221  * Adds an item to the collection.
12222  * @param {String} key The key to associate with the item
12223  * @param {Object} o The item to add.
12224  * @return {Object} The item added.
12225  */
12226     add : function(key, o){
12227         if(arguments.length == 1){
12228             o = arguments[0];
12229             key = this.getKey(o);
12230         }
12231         if(typeof key == "undefined" || key === null){
12232             this.length++;
12233             this.items.push(o);
12234             this.keys.push(null);
12235         }else{
12236             var old = this.map[key];
12237             if(old){
12238                 return this.replace(key, o);
12239             }
12240             this.length++;
12241             this.items.push(o);
12242             this.map[key] = o;
12243             this.keys.push(key);
12244         }
12245         this.fireEvent("add", this.length-1, o, key);
12246         return o;
12247     },
12248    
12249 /**
12250   * MixedCollection has a generic way to fetch keys if you implement getKey.
12251 <pre><code>
12252 // normal way
12253 var mc = new Roo.util.MixedCollection();
12254 mc.add(someEl.dom.id, someEl);
12255 mc.add(otherEl.dom.id, otherEl);
12256 //and so on
12257
12258 // using getKey
12259 var mc = new Roo.util.MixedCollection();
12260 mc.getKey = function(el){
12261    return el.dom.id;
12262 };
12263 mc.add(someEl);
12264 mc.add(otherEl);
12265
12266 // or via the constructor
12267 var mc = new Roo.util.MixedCollection(false, function(el){
12268    return el.dom.id;
12269 });
12270 mc.add(someEl);
12271 mc.add(otherEl);
12272 </code></pre>
12273  * @param o {Object} The item for which to find the key.
12274  * @return {Object} The key for the passed item.
12275  */
12276     getKey : function(o){
12277          return o.id; 
12278     },
12279    
12280 /**
12281  * Replaces an item in the collection.
12282  * @param {String} key The key associated with the item to replace, or the item to replace.
12283  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12284  * @return {Object}  The new item.
12285  */
12286     replace : function(key, o){
12287         if(arguments.length == 1){
12288             o = arguments[0];
12289             key = this.getKey(o);
12290         }
12291         var old = this.item(key);
12292         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12293              return this.add(key, o);
12294         }
12295         var index = this.indexOfKey(key);
12296         this.items[index] = o;
12297         this.map[key] = o;
12298         this.fireEvent("replace", key, old, o);
12299         return o;
12300     },
12301    
12302 /**
12303  * Adds all elements of an Array or an Object to the collection.
12304  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12305  * an Array of values, each of which are added to the collection.
12306  */
12307     addAll : function(objs){
12308         if(arguments.length > 1 || objs instanceof Array){
12309             var args = arguments.length > 1 ? arguments : objs;
12310             for(var i = 0, len = args.length; i < len; i++){
12311                 this.add(args[i]);
12312             }
12313         }else{
12314             for(var key in objs){
12315                 if(this.allowFunctions || typeof objs[key] != "function"){
12316                     this.add(key, objs[key]);
12317                 }
12318             }
12319         }
12320     },
12321    
12322 /**
12323  * Executes the specified function once for every item in the collection, passing each
12324  * item as the first and only parameter. returning false from the function will stop the iteration.
12325  * @param {Function} fn The function to execute for each item.
12326  * @param {Object} scope (optional) The scope in which to execute the function.
12327  */
12328     each : function(fn, scope){
12329         var items = [].concat(this.items); // each safe for removal
12330         for(var i = 0, len = items.length; i < len; i++){
12331             if(fn.call(scope || items[i], items[i], i, len) === false){
12332                 break;
12333             }
12334         }
12335     },
12336    
12337 /**
12338  * Executes the specified function once for every key in the collection, passing each
12339  * key, and its associated item as the first two parameters.
12340  * @param {Function} fn The function to execute for each item.
12341  * @param {Object} scope (optional) The scope in which to execute the function.
12342  */
12343     eachKey : function(fn, scope){
12344         for(var i = 0, len = this.keys.length; i < len; i++){
12345             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12346         }
12347     },
12348    
12349 /**
12350  * Returns the first item in the collection which elicits a true return value from the
12351  * passed selection function.
12352  * @param {Function} fn The selection function to execute for each item.
12353  * @param {Object} scope (optional) The scope in which to execute the function.
12354  * @return {Object} The first item in the collection which returned true from the selection function.
12355  */
12356     find : function(fn, scope){
12357         for(var i = 0, len = this.items.length; i < len; i++){
12358             if(fn.call(scope || window, this.items[i], this.keys[i])){
12359                 return this.items[i];
12360             }
12361         }
12362         return null;
12363     },
12364    
12365 /**
12366  * Inserts an item at the specified index in the collection.
12367  * @param {Number} index The index to insert the item at.
12368  * @param {String} key The key to associate with the new item, or the item itself.
12369  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12370  * @return {Object} The item inserted.
12371  */
12372     insert : function(index, key, o){
12373         if(arguments.length == 2){
12374             o = arguments[1];
12375             key = this.getKey(o);
12376         }
12377         if(index >= this.length){
12378             return this.add(key, o);
12379         }
12380         this.length++;
12381         this.items.splice(index, 0, o);
12382         if(typeof key != "undefined" && key != null){
12383             this.map[key] = o;
12384         }
12385         this.keys.splice(index, 0, key);
12386         this.fireEvent("add", index, o, key);
12387         return o;
12388     },
12389    
12390 /**
12391  * Removed an item from the collection.
12392  * @param {Object} o The item to remove.
12393  * @return {Object} The item removed.
12394  */
12395     remove : function(o){
12396         return this.removeAt(this.indexOf(o));
12397     },
12398    
12399 /**
12400  * Remove an item from a specified index in the collection.
12401  * @param {Number} index The index within the collection of the item to remove.
12402  */
12403     removeAt : function(index){
12404         if(index < this.length && index >= 0){
12405             this.length--;
12406             var o = this.items[index];
12407             this.items.splice(index, 1);
12408             var key = this.keys[index];
12409             if(typeof key != "undefined"){
12410                 delete this.map[key];
12411             }
12412             this.keys.splice(index, 1);
12413             this.fireEvent("remove", o, key);
12414         }
12415     },
12416    
12417 /**
12418  * Removed an item associated with the passed key fom the collection.
12419  * @param {String} key The key of the item to remove.
12420  */
12421     removeKey : function(key){
12422         return this.removeAt(this.indexOfKey(key));
12423     },
12424    
12425 /**
12426  * Returns the number of items in the collection.
12427  * @return {Number} the number of items in the collection.
12428  */
12429     getCount : function(){
12430         return this.length; 
12431     },
12432    
12433 /**
12434  * Returns index within the collection of the passed Object.
12435  * @param {Object} o The item to find the index of.
12436  * @return {Number} index of the item.
12437  */
12438     indexOf : function(o){
12439         if(!this.items.indexOf){
12440             for(var i = 0, len = this.items.length; i < len; i++){
12441                 if(this.items[i] == o) return i;
12442             }
12443             return -1;
12444         }else{
12445             return this.items.indexOf(o);
12446         }
12447     },
12448    
12449 /**
12450  * Returns index within the collection of the passed key.
12451  * @param {String} key The key to find the index of.
12452  * @return {Number} index of the key.
12453  */
12454     indexOfKey : function(key){
12455         if(!this.keys.indexOf){
12456             for(var i = 0, len = this.keys.length; i < len; i++){
12457                 if(this.keys[i] == key) return i;
12458             }
12459             return -1;
12460         }else{
12461             return this.keys.indexOf(key);
12462         }
12463     },
12464    
12465 /**
12466  * Returns the item associated with the passed key OR index. Key has priority over index.
12467  * @param {String/Number} key The key or index of the item.
12468  * @return {Object} The item associated with the passed key.
12469  */
12470     item : function(key){
12471         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12472         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12473     },
12474     
12475 /**
12476  * Returns the item at the specified index.
12477  * @param {Number} index The index of the item.
12478  * @return {Object}
12479  */
12480     itemAt : function(index){
12481         return this.items[index];
12482     },
12483     
12484 /**
12485  * Returns the item associated with the passed key.
12486  * @param {String/Number} key The key of the item.
12487  * @return {Object} The item associated with the passed key.
12488  */
12489     key : function(key){
12490         return this.map[key];
12491     },
12492    
12493 /**
12494  * Returns true if the collection contains the passed Object as an item.
12495  * @param {Object} o  The Object to look for in the collection.
12496  * @return {Boolean} True if the collection contains the Object as an item.
12497  */
12498     contains : function(o){
12499         return this.indexOf(o) != -1;
12500     },
12501    
12502 /**
12503  * Returns true if the collection contains the passed Object as a key.
12504  * @param {String} key The key to look for in the collection.
12505  * @return {Boolean} True if the collection contains the Object as a key.
12506  */
12507     containsKey : function(key){
12508         return typeof this.map[key] != "undefined";
12509     },
12510    
12511 /**
12512  * Removes all items from the collection.
12513  */
12514     clear : function(){
12515         this.length = 0;
12516         this.items = [];
12517         this.keys = [];
12518         this.map = {};
12519         this.fireEvent("clear");
12520     },
12521    
12522 /**
12523  * Returns the first item in the collection.
12524  * @return {Object} the first item in the collection..
12525  */
12526     first : function(){
12527         return this.items[0]; 
12528     },
12529    
12530 /**
12531  * Returns the last item in the collection.
12532  * @return {Object} the last item in the collection..
12533  */
12534     last : function(){
12535         return this.items[this.length-1];   
12536     },
12537     
12538     _sort : function(property, dir, fn){
12539         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12540         fn = fn || function(a, b){
12541             return a-b;
12542         };
12543         var c = [], k = this.keys, items = this.items;
12544         for(var i = 0, len = items.length; i < len; i++){
12545             c[c.length] = {key: k[i], value: items[i], index: i};
12546         }
12547         c.sort(function(a, b){
12548             var v = fn(a[property], b[property]) * dsc;
12549             if(v == 0){
12550                 v = (a.index < b.index ? -1 : 1);
12551             }
12552             return v;
12553         });
12554         for(var i = 0, len = c.length; i < len; i++){
12555             items[i] = c[i].value;
12556             k[i] = c[i].key;
12557         }
12558         this.fireEvent("sort", this);
12559     },
12560     
12561     /**
12562      * Sorts this collection with the passed comparison function
12563      * @param {String} direction (optional) "ASC" or "DESC"
12564      * @param {Function} fn (optional) comparison function
12565      */
12566     sort : function(dir, fn){
12567         this._sort("value", dir, fn);
12568     },
12569     
12570     /**
12571      * Sorts this collection by keys
12572      * @param {String} direction (optional) "ASC" or "DESC"
12573      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12574      */
12575     keySort : function(dir, fn){
12576         this._sort("key", dir, fn || function(a, b){
12577             return String(a).toUpperCase()-String(b).toUpperCase();
12578         });
12579     },
12580     
12581     /**
12582      * Returns a range of items in this collection
12583      * @param {Number} startIndex (optional) defaults to 0
12584      * @param {Number} endIndex (optional) default to the last item
12585      * @return {Array} An array of items
12586      */
12587     getRange : function(start, end){
12588         var items = this.items;
12589         if(items.length < 1){
12590             return [];
12591         }
12592         start = start || 0;
12593         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12594         var r = [];
12595         if(start <= end){
12596             for(var i = start; i <= end; i++) {
12597                     r[r.length] = items[i];
12598             }
12599         }else{
12600             for(var i = start; i >= end; i--) {
12601                     r[r.length] = items[i];
12602             }
12603         }
12604         return r;
12605     },
12606         
12607     /**
12608      * Filter the <i>objects</i> in this collection by a specific property. 
12609      * Returns a new collection that has been filtered.
12610      * @param {String} property A property on your objects
12611      * @param {String/RegExp} value Either string that the property values 
12612      * should start with or a RegExp to test against the property
12613      * @return {MixedCollection} The new filtered collection
12614      */
12615     filter : function(property, value){
12616         if(!value.exec){ // not a regex
12617             value = String(value);
12618             if(value.length == 0){
12619                 return this.clone();
12620             }
12621             value = new RegExp("^" + Roo.escapeRe(value), "i");
12622         }
12623         return this.filterBy(function(o){
12624             return o && value.test(o[property]);
12625         });
12626         },
12627     
12628     /**
12629      * Filter by a function. * Returns a new collection that has been filtered.
12630      * The passed function will be called with each 
12631      * object in the collection. If the function returns true, the value is included 
12632      * otherwise it is filtered.
12633      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12634      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12635      * @return {MixedCollection} The new filtered collection
12636      */
12637     filterBy : function(fn, scope){
12638         var r = new Roo.util.MixedCollection();
12639         r.getKey = this.getKey;
12640         var k = this.keys, it = this.items;
12641         for(var i = 0, len = it.length; i < len; i++){
12642             if(fn.call(scope||this, it[i], k[i])){
12643                                 r.add(k[i], it[i]);
12644                         }
12645         }
12646         return r;
12647     },
12648     
12649     /**
12650      * Creates a duplicate of this collection
12651      * @return {MixedCollection}
12652      */
12653     clone : function(){
12654         var r = new Roo.util.MixedCollection();
12655         var k = this.keys, it = this.items;
12656         for(var i = 0, len = it.length; i < len; i++){
12657             r.add(k[i], it[i]);
12658         }
12659         r.getKey = this.getKey;
12660         return r;
12661     }
12662 });
12663 /**
12664  * Returns the item associated with the passed key or index.
12665  * @method
12666  * @param {String/Number} key The key or index of the item.
12667  * @return {Object} The item associated with the passed key.
12668  */
12669 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12670  * Based on:
12671  * Ext JS Library 1.1.1
12672  * Copyright(c) 2006-2007, Ext JS, LLC.
12673  *
12674  * Originally Released Under LGPL - original licence link has changed is not relivant.
12675  *
12676  * Fork - LGPL
12677  * <script type="text/javascript">
12678  */
12679 /**
12680  * @class Roo.util.JSON
12681  * Modified version of Douglas Crockford"s json.js that doesn"t
12682  * mess with the Object prototype 
12683  * http://www.json.org/js.html
12684  * @singleton
12685  */
12686 Roo.util.JSON = new (function(){
12687     var useHasOwn = {}.hasOwnProperty ? true : false;
12688     
12689     // crashes Safari in some instances
12690     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12691     
12692     var pad = function(n) {
12693         return n < 10 ? "0" + n : n;
12694     };
12695     
12696     var m = {
12697         "\b": '\\b',
12698         "\t": '\\t',
12699         "\n": '\\n',
12700         "\f": '\\f',
12701         "\r": '\\r',
12702         '"' : '\\"',
12703         "\\": '\\\\'
12704     };
12705
12706     var encodeString = function(s){
12707         if (/["\\\x00-\x1f]/.test(s)) {
12708             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12709                 var c = m[b];
12710                 if(c){
12711                     return c;
12712                 }
12713                 c = b.charCodeAt();
12714                 return "\\u00" +
12715                     Math.floor(c / 16).toString(16) +
12716                     (c % 16).toString(16);
12717             }) + '"';
12718         }
12719         return '"' + s + '"';
12720     };
12721     
12722     var encodeArray = function(o){
12723         var a = ["["], b, i, l = o.length, v;
12724             for (i = 0; i < l; i += 1) {
12725                 v = o[i];
12726                 switch (typeof v) {
12727                     case "undefined":
12728                     case "function":
12729                     case "unknown":
12730                         break;
12731                     default:
12732                         if (b) {
12733                             a.push(',');
12734                         }
12735                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12736                         b = true;
12737                 }
12738             }
12739             a.push("]");
12740             return a.join("");
12741     };
12742     
12743     var encodeDate = function(o){
12744         return '"' + o.getFullYear() + "-" +
12745                 pad(o.getMonth() + 1) + "-" +
12746                 pad(o.getDate()) + "T" +
12747                 pad(o.getHours()) + ":" +
12748                 pad(o.getMinutes()) + ":" +
12749                 pad(o.getSeconds()) + '"';
12750     };
12751     
12752     /**
12753      * Encodes an Object, Array or other value
12754      * @param {Mixed} o The variable to encode
12755      * @return {String} The JSON string
12756      */
12757     this.encode = function(o){
12758         if(typeof o == "undefined" || o === null){
12759             return "null";
12760         }else if(o instanceof Array){
12761             return encodeArray(o);
12762         }else if(o instanceof Date){
12763             return encodeDate(o);
12764         }else if(typeof o == "string"){
12765             return encodeString(o);
12766         }else if(typeof o == "number"){
12767             return isFinite(o) ? String(o) : "null";
12768         }else if(typeof o == "boolean"){
12769             return String(o);
12770         }else {
12771             var a = ["{"], b, i, v;
12772             for (i in o) {
12773                 if(!useHasOwn || o.hasOwnProperty(i)) {
12774                     v = o[i];
12775                     switch (typeof v) {
12776                     case "undefined":
12777                     case "function":
12778                     case "unknown":
12779                         break;
12780                     default:
12781                         if(b){
12782                             a.push(',');
12783                         }
12784                         a.push(this.encode(i), ":",
12785                                 v === null ? "null" : this.encode(v));
12786                         b = true;
12787                     }
12788                 }
12789             }
12790             a.push("}");
12791             return a.join("");
12792         }
12793     };
12794     
12795     /**
12796      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12797      * @param {String} json The JSON string
12798      * @return {Object} The resulting object
12799      */
12800     this.decode = function(json){
12801         /**
12802          * eval:var:json
12803          */
12804         return eval("(" + json + ')');
12805     };
12806 })();
12807 /** 
12808  * Shorthand for {@link Roo.util.JSON#encode}
12809  * @member Roo encode 
12810  * @method */
12811 Roo.encode = Roo.util.JSON.encode;
12812 /** 
12813  * Shorthand for {@link Roo.util.JSON#decode}
12814  * @member Roo decode 
12815  * @method */
12816 Roo.decode = Roo.util.JSON.decode;
12817 /*
12818  * Based on:
12819  * Ext JS Library 1.1.1
12820  * Copyright(c) 2006-2007, Ext JS, LLC.
12821  *
12822  * Originally Released Under LGPL - original licence link has changed is not relivant.
12823  *
12824  * Fork - LGPL
12825  * <script type="text/javascript">
12826  */
12827  
12828 /**
12829  * @class Roo.util.Format
12830  * Reusable data formatting functions
12831  * @singleton
12832  */
12833 Roo.util.Format = function(){
12834     var trimRe = /^\s+|\s+$/g;
12835     return {
12836         /**
12837          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12838          * @param {String} value The string to truncate
12839          * @param {Number} length The maximum length to allow before truncating
12840          * @return {String} The converted text
12841          */
12842         ellipsis : function(value, len){
12843             if(value && value.length > len){
12844                 return value.substr(0, len-3)+"...";
12845             }
12846             return value;
12847         },
12848
12849         /**
12850          * Checks a reference and converts it to empty string if it is undefined
12851          * @param {Mixed} value Reference to check
12852          * @return {Mixed} Empty string if converted, otherwise the original value
12853          */
12854         undef : function(value){
12855             return typeof value != "undefined" ? value : "";
12856         },
12857
12858         /**
12859          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12860          * @param {String} value The string to encode
12861          * @return {String} The encoded text
12862          */
12863         htmlEncode : function(value){
12864             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12865         },
12866
12867         /**
12868          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12869          * @param {String} value The string to decode
12870          * @return {String} The decoded text
12871          */
12872         htmlDecode : function(value){
12873             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12874         },
12875
12876         /**
12877          * Trims any whitespace from either side of a string
12878          * @param {String} value The text to trim
12879          * @return {String} The trimmed text
12880          */
12881         trim : function(value){
12882             return String(value).replace(trimRe, "");
12883         },
12884
12885         /**
12886          * Returns a substring from within an original string
12887          * @param {String} value The original text
12888          * @param {Number} start The start index of the substring
12889          * @param {Number} length The length of the substring
12890          * @return {String} The substring
12891          */
12892         substr : function(value, start, length){
12893             return String(value).substr(start, length);
12894         },
12895
12896         /**
12897          * Converts a string to all lower case letters
12898          * @param {String} value The text to convert
12899          * @return {String} The converted text
12900          */
12901         lowercase : function(value){
12902             return String(value).toLowerCase();
12903         },
12904
12905         /**
12906          * Converts a string to all upper case letters
12907          * @param {String} value The text to convert
12908          * @return {String} The converted text
12909          */
12910         uppercase : function(value){
12911             return String(value).toUpperCase();
12912         },
12913
12914         /**
12915          * Converts the first character only of a string to upper case
12916          * @param {String} value The text to convert
12917          * @return {String} The converted text
12918          */
12919         capitalize : function(value){
12920             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12921         },
12922
12923         // private
12924         call : function(value, fn){
12925             if(arguments.length > 2){
12926                 var args = Array.prototype.slice.call(arguments, 2);
12927                 args.unshift(value);
12928                  
12929                 return /** eval:var:value */  eval(fn).apply(window, args);
12930             }else{
12931                 /** eval:var:value */
12932                 return /** eval:var:value */ eval(fn).call(window, value);
12933             }
12934         },
12935
12936         /**
12937          * Format a number as US currency
12938          * @param {Number/String} value The numeric value to format
12939          * @return {String} The formatted currency string
12940          */
12941         usMoney : function(v){
12942             v = (Math.round((v-0)*100))/100;
12943             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12944             v = String(v);
12945             var ps = v.split('.');
12946             var whole = ps[0];
12947             var sub = ps[1] ? '.'+ ps[1] : '.00';
12948             var r = /(\d+)(\d{3})/;
12949             while (r.test(whole)) {
12950                 whole = whole.replace(r, '$1' + ',' + '$2');
12951             }
12952             return "$" + whole + sub ;
12953         },
12954
12955         /**
12956          * Parse a value into a formatted date using the specified format pattern.
12957          * @param {Mixed} value The value to format
12958          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12959          * @return {String} The formatted date string
12960          */
12961         date : function(v, format){
12962             if(!v){
12963                 return "";
12964             }
12965             if(!(v instanceof Date)){
12966                 v = new Date(Date.parse(v));
12967             }
12968             return v.dateFormat(format || "m/d/Y");
12969         },
12970
12971         /**
12972          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12973          * @param {String} format Any valid date format string
12974          * @return {Function} The date formatting function
12975          */
12976         dateRenderer : function(format){
12977             return function(v){
12978                 return Roo.util.Format.date(v, format);  
12979             };
12980         },
12981
12982         // private
12983         stripTagsRE : /<\/?[^>]+>/gi,
12984         
12985         /**
12986          * Strips all HTML tags
12987          * @param {Mixed} value The text from which to strip tags
12988          * @return {String} The stripped text
12989          */
12990         stripTags : function(v){
12991             return !v ? v : String(v).replace(this.stripTagsRE, "");
12992         }
12993     };
12994 }();/*
12995  * Based on:
12996  * Ext JS Library 1.1.1
12997  * Copyright(c) 2006-2007, Ext JS, LLC.
12998  *
12999  * Originally Released Under LGPL - original licence link has changed is not relivant.
13000  *
13001  * Fork - LGPL
13002  * <script type="text/javascript">
13003  */
13004
13005
13006  
13007
13008 /**
13009  * @class Roo.MasterTemplate
13010  * @extends Roo.Template
13011  * Provides a template that can have child templates. The syntax is:
13012 <pre><code>
13013 var t = new Roo.MasterTemplate(
13014         '&lt;select name="{name}"&gt;',
13015                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13016         '&lt;/select&gt;'
13017 );
13018 t.add('options', {value: 'foo', text: 'bar'});
13019 // or you can add multiple child elements in one shot
13020 t.addAll('options', [
13021     {value: 'foo', text: 'bar'},
13022     {value: 'foo2', text: 'bar2'},
13023     {value: 'foo3', text: 'bar3'}
13024 ]);
13025 // then append, applying the master template values
13026 t.append('my-form', {name: 'my-select'});
13027 </code></pre>
13028 * A name attribute for the child template is not required if you have only one child
13029 * template or you want to refer to them by index.
13030  */
13031 Roo.MasterTemplate = function(){
13032     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13033     this.originalHtml = this.html;
13034     var st = {};
13035     var m, re = this.subTemplateRe;
13036     re.lastIndex = 0;
13037     var subIndex = 0;
13038     while(m = re.exec(this.html)){
13039         var name = m[1], content = m[2];
13040         st[subIndex] = {
13041             name: name,
13042             index: subIndex,
13043             buffer: [],
13044             tpl : new Roo.Template(content)
13045         };
13046         if(name){
13047             st[name] = st[subIndex];
13048         }
13049         st[subIndex].tpl.compile();
13050         st[subIndex].tpl.call = this.call.createDelegate(this);
13051         subIndex++;
13052     }
13053     this.subCount = subIndex;
13054     this.subs = st;
13055 };
13056 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13057     /**
13058     * The regular expression used to match sub templates
13059     * @type RegExp
13060     * @property
13061     */
13062     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13063
13064     /**
13065      * Applies the passed values to a child template.
13066      * @param {String/Number} name (optional) The name or index of the child template
13067      * @param {Array/Object} values The values to be applied to the template
13068      * @return {MasterTemplate} this
13069      */
13070      add : function(name, values){
13071         if(arguments.length == 1){
13072             values = arguments[0];
13073             name = 0;
13074         }
13075         var s = this.subs[name];
13076         s.buffer[s.buffer.length] = s.tpl.apply(values);
13077         return this;
13078     },
13079
13080     /**
13081      * Applies all the passed values to a child template.
13082      * @param {String/Number} name (optional) The name or index of the child template
13083      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13084      * @param {Boolean} reset (optional) True to reset the template first
13085      * @return {MasterTemplate} this
13086      */
13087     fill : function(name, values, reset){
13088         var a = arguments;
13089         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13090             values = a[0];
13091             name = 0;
13092             reset = a[1];
13093         }
13094         if(reset){
13095             this.reset();
13096         }
13097         for(var i = 0, len = values.length; i < len; i++){
13098             this.add(name, values[i]);
13099         }
13100         return this;
13101     },
13102
13103     /**
13104      * Resets the template for reuse
13105      * @return {MasterTemplate} this
13106      */
13107      reset : function(){
13108         var s = this.subs;
13109         for(var i = 0; i < this.subCount; i++){
13110             s[i].buffer = [];
13111         }
13112         return this;
13113     },
13114
13115     applyTemplate : function(values){
13116         var s = this.subs;
13117         var replaceIndex = -1;
13118         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13119             return s[++replaceIndex].buffer.join("");
13120         });
13121         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13122     },
13123
13124     apply : function(){
13125         return this.applyTemplate.apply(this, arguments);
13126     },
13127
13128     compile : function(){return this;}
13129 });
13130
13131 /**
13132  * Alias for fill().
13133  * @method
13134  */
13135 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13136  /**
13137  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13138  * var tpl = Roo.MasterTemplate.from('element-id');
13139  * @param {String/HTMLElement} el
13140  * @param {Object} config
13141  * @static
13142  */
13143 Roo.MasterTemplate.from = function(el, config){
13144     el = Roo.getDom(el);
13145     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13146 };/*
13147  * Based on:
13148  * Ext JS Library 1.1.1
13149  * Copyright(c) 2006-2007, Ext JS, LLC.
13150  *
13151  * Originally Released Under LGPL - original licence link has changed is not relivant.
13152  *
13153  * Fork - LGPL
13154  * <script type="text/javascript">
13155  */
13156
13157  
13158 /**
13159  * @class Roo.util.CSS
13160  * Utility class for manipulating CSS rules
13161  * @singleton
13162  */
13163 Roo.util.CSS = function(){
13164         var rules = null;
13165         var doc = document;
13166
13167     var camelRe = /(-[a-z])/gi;
13168     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13169
13170    return {
13171    /**
13172     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13173     * tag and appended to the HEAD of the document.
13174     * @param {String} cssText The text containing the css rules
13175     * @param {String} id An id to add to the stylesheet for later removal
13176     * @return {StyleSheet}
13177     */
13178    createStyleSheet : function(cssText, id){
13179        var ss;
13180        var head = doc.getElementsByTagName("head")[0];
13181        var rules = doc.createElement("style");
13182        rules.setAttribute("type", "text/css");
13183        if(id){
13184            rules.setAttribute("id", id);
13185        }
13186        if(Roo.isIE){
13187            head.appendChild(rules);
13188            ss = rules.styleSheet;
13189            ss.cssText = cssText;
13190        }else{
13191            try{
13192                 rules.appendChild(doc.createTextNode(cssText));
13193            }catch(e){
13194                rules.cssText = cssText; 
13195            }
13196            head.appendChild(rules);
13197            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13198        }
13199        this.cacheStyleSheet(ss);
13200        return ss;
13201    },
13202
13203    /**
13204     * Removes a style or link tag by id
13205     * @param {String} id The id of the tag
13206     */
13207    removeStyleSheet : function(id){
13208        var existing = doc.getElementById(id);
13209        if(existing){
13210            existing.parentNode.removeChild(existing);
13211        }
13212    },
13213
13214    /**
13215     * Dynamically swaps an existing stylesheet reference for a new one
13216     * @param {String} id The id of an existing link tag to remove
13217     * @param {String} url The href of the new stylesheet to include
13218     */
13219    swapStyleSheet : function(id, url){
13220        this.removeStyleSheet(id);
13221        var ss = doc.createElement("link");
13222        ss.setAttribute("rel", "stylesheet");
13223        ss.setAttribute("type", "text/css");
13224        ss.setAttribute("id", id);
13225        ss.setAttribute("href", url);
13226        doc.getElementsByTagName("head")[0].appendChild(ss);
13227    },
13228    
13229    /**
13230     * Refresh the rule cache if you have dynamically added stylesheets
13231     * @return {Object} An object (hash) of rules indexed by selector
13232     */
13233    refreshCache : function(){
13234        return this.getRules(true);
13235    },
13236
13237    // private
13238    cacheStyleSheet : function(ss){
13239        if(!rules){
13240            rules = {};
13241        }
13242        try{// try catch for cross domain access issue
13243            var ssRules = ss.cssRules || ss.rules;
13244            for(var j = ssRules.length-1; j >= 0; --j){
13245                rules[ssRules[j].selectorText] = ssRules[j];
13246            }
13247        }catch(e){}
13248    },
13249    
13250    /**
13251     * Gets all css rules for the document
13252     * @param {Boolean} refreshCache true to refresh the internal cache
13253     * @return {Object} An object (hash) of rules indexed by selector
13254     */
13255    getRules : function(refreshCache){
13256                 if(rules == null || refreshCache){
13257                         rules = {};
13258                         var ds = doc.styleSheets;
13259                         for(var i =0, len = ds.length; i < len; i++){
13260                             try{
13261                         this.cacheStyleSheet(ds[i]);
13262                     }catch(e){} 
13263                 }
13264                 }
13265                 return rules;
13266         },
13267         
13268         /**
13269     * Gets an an individual CSS rule by selector(s)
13270     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13271     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13272     * @return {CSSRule} The CSS rule or null if one is not found
13273     */
13274    getRule : function(selector, refreshCache){
13275                 var rs = this.getRules(refreshCache);
13276                 if(!(selector instanceof Array)){
13277                     return rs[selector];
13278                 }
13279                 for(var i = 0; i < selector.length; i++){
13280                         if(rs[selector[i]]){
13281                                 return rs[selector[i]];
13282                         }
13283                 }
13284                 return null;
13285         },
13286         
13287         
13288         /**
13289     * Updates a rule property
13290     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13291     * @param {String} property The css property
13292     * @param {String} value The new value for the property
13293     * @return {Boolean} true If a rule was found and updated
13294     */
13295    updateRule : function(selector, property, value){
13296                 if(!(selector instanceof Array)){
13297                         var rule = this.getRule(selector);
13298                         if(rule){
13299                                 rule.style[property.replace(camelRe, camelFn)] = value;
13300                                 return true;
13301                         }
13302                 }else{
13303                         for(var i = 0; i < selector.length; i++){
13304                                 if(this.updateRule(selector[i], property, value)){
13305                                         return true;
13306                                 }
13307                         }
13308                 }
13309                 return false;
13310         }
13311    };   
13312 }();/*
13313  * Based on:
13314  * Ext JS Library 1.1.1
13315  * Copyright(c) 2006-2007, Ext JS, LLC.
13316  *
13317  * Originally Released Under LGPL - original licence link has changed is not relivant.
13318  *
13319  * Fork - LGPL
13320  * <script type="text/javascript">
13321  */
13322
13323  
13324
13325 /**
13326  * @class Roo.util.ClickRepeater
13327  * @extends Roo.util.Observable
13328  * 
13329  * A wrapper class which can be applied to any element. Fires a "click" event while the
13330  * mouse is pressed. The interval between firings may be specified in the config but
13331  * defaults to 10 milliseconds.
13332  * 
13333  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13334  * 
13335  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13336  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13337  * Similar to an autorepeat key delay.
13338  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13339  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13340  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13341  *           "interval" and "delay" are ignored. "immediate" is honored.
13342  * @cfg {Boolean} preventDefault True to prevent the default click event
13343  * @cfg {Boolean} stopDefault True to stop the default click event
13344  * 
13345  * @history
13346  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13347  *     2007-02-02 jvs Renamed to ClickRepeater
13348  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13349  *
13350  *  @constructor
13351  * @param {String/HTMLElement/Element} el The element to listen on
13352  * @param {Object} config
13353  **/
13354 Roo.util.ClickRepeater = function(el, config)
13355 {
13356     this.el = Roo.get(el);
13357     this.el.unselectable();
13358
13359     Roo.apply(this, config);
13360
13361     this.addEvents({
13362     /**
13363      * @event mousedown
13364      * Fires when the mouse button is depressed.
13365      * @param {Roo.util.ClickRepeater} this
13366      */
13367         "mousedown" : true,
13368     /**
13369      * @event click
13370      * Fires on a specified interval during the time the element is pressed.
13371      * @param {Roo.util.ClickRepeater} this
13372      */
13373         "click" : true,
13374     /**
13375      * @event mouseup
13376      * Fires when the mouse key is released.
13377      * @param {Roo.util.ClickRepeater} this
13378      */
13379         "mouseup" : true
13380     });
13381
13382     this.el.on("mousedown", this.handleMouseDown, this);
13383     if(this.preventDefault || this.stopDefault){
13384         this.el.on("click", function(e){
13385             if(this.preventDefault){
13386                 e.preventDefault();
13387             }
13388             if(this.stopDefault){
13389                 e.stopEvent();
13390             }
13391         }, this);
13392     }
13393
13394     // allow inline handler
13395     if(this.handler){
13396         this.on("click", this.handler,  this.scope || this);
13397     }
13398
13399     Roo.util.ClickRepeater.superclass.constructor.call(this);
13400 };
13401
13402 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13403     interval : 20,
13404     delay: 250,
13405     preventDefault : true,
13406     stopDefault : false,
13407     timer : 0,
13408
13409     // private
13410     handleMouseDown : function(){
13411         clearTimeout(this.timer);
13412         this.el.blur();
13413         if(this.pressClass){
13414             this.el.addClass(this.pressClass);
13415         }
13416         this.mousedownTime = new Date();
13417
13418         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13419         this.el.on("mouseout", this.handleMouseOut, this);
13420
13421         this.fireEvent("mousedown", this);
13422         this.fireEvent("click", this);
13423         
13424         this.timer = this.click.defer(this.delay || this.interval, this);
13425     },
13426
13427     // private
13428     click : function(){
13429         this.fireEvent("click", this);
13430         this.timer = this.click.defer(this.getInterval(), this);
13431     },
13432
13433     // private
13434     getInterval: function(){
13435         if(!this.accelerate){
13436             return this.interval;
13437         }
13438         var pressTime = this.mousedownTime.getElapsed();
13439         if(pressTime < 500){
13440             return 400;
13441         }else if(pressTime < 1700){
13442             return 320;
13443         }else if(pressTime < 2600){
13444             return 250;
13445         }else if(pressTime < 3500){
13446             return 180;
13447         }else if(pressTime < 4400){
13448             return 140;
13449         }else if(pressTime < 5300){
13450             return 80;
13451         }else if(pressTime < 6200){
13452             return 50;
13453         }else{
13454             return 10;
13455         }
13456     },
13457
13458     // private
13459     handleMouseOut : function(){
13460         clearTimeout(this.timer);
13461         if(this.pressClass){
13462             this.el.removeClass(this.pressClass);
13463         }
13464         this.el.on("mouseover", this.handleMouseReturn, this);
13465     },
13466
13467     // private
13468     handleMouseReturn : function(){
13469         this.el.un("mouseover", this.handleMouseReturn);
13470         if(this.pressClass){
13471             this.el.addClass(this.pressClass);
13472         }
13473         this.click();
13474     },
13475
13476     // private
13477     handleMouseUp : function(){
13478         clearTimeout(this.timer);
13479         this.el.un("mouseover", this.handleMouseReturn);
13480         this.el.un("mouseout", this.handleMouseOut);
13481         Roo.get(document).un("mouseup", this.handleMouseUp);
13482         this.el.removeClass(this.pressClass);
13483         this.fireEvent("mouseup", this);
13484     }
13485 });/*
13486  * Based on:
13487  * Ext JS Library 1.1.1
13488  * Copyright(c) 2006-2007, Ext JS, LLC.
13489  *
13490  * Originally Released Under LGPL - original licence link has changed is not relivant.
13491  *
13492  * Fork - LGPL
13493  * <script type="text/javascript">
13494  */
13495
13496  
13497 /**
13498  * @class Roo.KeyNav
13499  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13500  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13501  * way to implement custom navigation schemes for any UI component.</p>
13502  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13503  * pageUp, pageDown, del, home, end.  Usage:</p>
13504  <pre><code>
13505 var nav = new Roo.KeyNav("my-element", {
13506     "left" : function(e){
13507         this.moveLeft(e.ctrlKey);
13508     },
13509     "right" : function(e){
13510         this.moveRight(e.ctrlKey);
13511     },
13512     "enter" : function(e){
13513         this.save();
13514     },
13515     scope : this
13516 });
13517 </code></pre>
13518  * @constructor
13519  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13520  * @param {Object} config The config
13521  */
13522 Roo.KeyNav = function(el, config){
13523     this.el = Roo.get(el);
13524     Roo.apply(this, config);
13525     if(!this.disabled){
13526         this.disabled = true;
13527         this.enable();
13528     }
13529 };
13530
13531 Roo.KeyNav.prototype = {
13532     /**
13533      * @cfg {Boolean} disabled
13534      * True to disable this KeyNav instance (defaults to false)
13535      */
13536     disabled : false,
13537     /**
13538      * @cfg {String} defaultEventAction
13539      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13540      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13541      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13542      */
13543     defaultEventAction: "stopEvent",
13544     /**
13545      * @cfg {Boolean} forceKeyDown
13546      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13547      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13548      * handle keydown instead of keypress.
13549      */
13550     forceKeyDown : false,
13551
13552     // private
13553     prepareEvent : function(e){
13554         var k = e.getKey();
13555         var h = this.keyToHandler[k];
13556         //if(h && this[h]){
13557         //    e.stopPropagation();
13558         //}
13559         if(Roo.isSafari && h && k >= 37 && k <= 40){
13560             e.stopEvent();
13561         }
13562     },
13563
13564     // private
13565     relay : function(e){
13566         var k = e.getKey();
13567         var h = this.keyToHandler[k];
13568         if(h && this[h]){
13569             if(this.doRelay(e, this[h], h) !== true){
13570                 e[this.defaultEventAction]();
13571             }
13572         }
13573     },
13574
13575     // private
13576     doRelay : function(e, h, hname){
13577         return h.call(this.scope || this, e);
13578     },
13579
13580     // possible handlers
13581     enter : false,
13582     left : false,
13583     right : false,
13584     up : false,
13585     down : false,
13586     tab : false,
13587     esc : false,
13588     pageUp : false,
13589     pageDown : false,
13590     del : false,
13591     home : false,
13592     end : false,
13593
13594     // quick lookup hash
13595     keyToHandler : {
13596         37 : "left",
13597         39 : "right",
13598         38 : "up",
13599         40 : "down",
13600         33 : "pageUp",
13601         34 : "pageDown",
13602         46 : "del",
13603         36 : "home",
13604         35 : "end",
13605         13 : "enter",
13606         27 : "esc",
13607         9  : "tab"
13608     },
13609
13610         /**
13611          * Enable this KeyNav
13612          */
13613         enable: function(){
13614                 if(this.disabled){
13615             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13616             // the EventObject will normalize Safari automatically
13617             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13618                 this.el.on("keydown", this.relay,  this);
13619             }else{
13620                 this.el.on("keydown", this.prepareEvent,  this);
13621                 this.el.on("keypress", this.relay,  this);
13622             }
13623                     this.disabled = false;
13624                 }
13625         },
13626
13627         /**
13628          * Disable this KeyNav
13629          */
13630         disable: function(){
13631                 if(!this.disabled){
13632                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13633                 this.el.un("keydown", this.relay);
13634             }else{
13635                 this.el.un("keydown", this.prepareEvent);
13636                 this.el.un("keypress", this.relay);
13637             }
13638                     this.disabled = true;
13639                 }
13640         }
13641 };/*
13642  * Based on:
13643  * Ext JS Library 1.1.1
13644  * Copyright(c) 2006-2007, Ext JS, LLC.
13645  *
13646  * Originally Released Under LGPL - original licence link has changed is not relivant.
13647  *
13648  * Fork - LGPL
13649  * <script type="text/javascript">
13650  */
13651
13652  
13653 /**
13654  * @class Roo.KeyMap
13655  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13656  * The constructor accepts the same config object as defined by {@link #addBinding}.
13657  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13658  * combination it will call the function with this signature (if the match is a multi-key
13659  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13660  * A KeyMap can also handle a string representation of keys.<br />
13661  * Usage:
13662  <pre><code>
13663 // map one key by key code
13664 var map = new Roo.KeyMap("my-element", {
13665     key: 13, // or Roo.EventObject.ENTER
13666     fn: myHandler,
13667     scope: myObject
13668 });
13669
13670 // map multiple keys to one action by string
13671 var map = new Roo.KeyMap("my-element", {
13672     key: "a\r\n\t",
13673     fn: myHandler,
13674     scope: myObject
13675 });
13676
13677 // map multiple keys to multiple actions by strings and array of codes
13678 var map = new Roo.KeyMap("my-element", [
13679     {
13680         key: [10,13],
13681         fn: function(){ alert("Return was pressed"); }
13682     }, {
13683         key: "abc",
13684         fn: function(){ alert('a, b or c was pressed'); }
13685     }, {
13686         key: "\t",
13687         ctrl:true,
13688         shift:true,
13689         fn: function(){ alert('Control + shift + tab was pressed.'); }
13690     }
13691 ]);
13692 </code></pre>
13693  * <b>Note: A KeyMap starts enabled</b>
13694  * @constructor
13695  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13696  * @param {Object} config The config (see {@link #addBinding})
13697  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13698  */
13699 Roo.KeyMap = function(el, config, eventName){
13700     this.el  = Roo.get(el);
13701     this.eventName = eventName || "keydown";
13702     this.bindings = [];
13703     if(config){
13704         this.addBinding(config);
13705     }
13706     this.enable();
13707 };
13708
13709 Roo.KeyMap.prototype = {
13710     /**
13711      * True to stop the event from bubbling and prevent the default browser action if the
13712      * key was handled by the KeyMap (defaults to false)
13713      * @type Boolean
13714      */
13715     stopEvent : false,
13716
13717     /**
13718      * Add a new binding to this KeyMap. The following config object properties are supported:
13719      * <pre>
13720 Property    Type             Description
13721 ----------  ---------------  ----------------------------------------------------------------------
13722 key         String/Array     A single keycode or an array of keycodes to handle
13723 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13724 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13725 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13726 fn          Function         The function to call when KeyMap finds the expected key combination
13727 scope       Object           The scope of the callback function
13728 </pre>
13729      *
13730      * Usage:
13731      * <pre><code>
13732 // Create a KeyMap
13733 var map = new Roo.KeyMap(document, {
13734     key: Roo.EventObject.ENTER,
13735     fn: handleKey,
13736     scope: this
13737 });
13738
13739 //Add a new binding to the existing KeyMap later
13740 map.addBinding({
13741     key: 'abc',
13742     shift: true,
13743     fn: handleKey,
13744     scope: this
13745 });
13746 </code></pre>
13747      * @param {Object/Array} config A single KeyMap config or an array of configs
13748      */
13749         addBinding : function(config){
13750         if(config instanceof Array){
13751             for(var i = 0, len = config.length; i < len; i++){
13752                 this.addBinding(config[i]);
13753             }
13754             return;
13755         }
13756         var keyCode = config.key,
13757             shift = config.shift, 
13758             ctrl = config.ctrl, 
13759             alt = config.alt,
13760             fn = config.fn,
13761             scope = config.scope;
13762         if(typeof keyCode == "string"){
13763             var ks = [];
13764             var keyString = keyCode.toUpperCase();
13765             for(var j = 0, len = keyString.length; j < len; j++){
13766                 ks.push(keyString.charCodeAt(j));
13767             }
13768             keyCode = ks;
13769         }
13770         var keyArray = keyCode instanceof Array;
13771         var handler = function(e){
13772             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13773                 var k = e.getKey();
13774                 if(keyArray){
13775                     for(var i = 0, len = keyCode.length; i < len; i++){
13776                         if(keyCode[i] == k){
13777                           if(this.stopEvent){
13778                               e.stopEvent();
13779                           }
13780                           fn.call(scope || window, k, e);
13781                           return;
13782                         }
13783                     }
13784                 }else{
13785                     if(k == keyCode){
13786                         if(this.stopEvent){
13787                            e.stopEvent();
13788                         }
13789                         fn.call(scope || window, k, e);
13790                     }
13791                 }
13792             }
13793         };
13794         this.bindings.push(handler);  
13795         },
13796
13797     /**
13798      * Shorthand for adding a single key listener
13799      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13800      * following options:
13801      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13802      * @param {Function} fn The function to call
13803      * @param {Object} scope (optional) The scope of the function
13804      */
13805     on : function(key, fn, scope){
13806         var keyCode, shift, ctrl, alt;
13807         if(typeof key == "object" && !(key instanceof Array)){
13808             keyCode = key.key;
13809             shift = key.shift;
13810             ctrl = key.ctrl;
13811             alt = key.alt;
13812         }else{
13813             keyCode = key;
13814         }
13815         this.addBinding({
13816             key: keyCode,
13817             shift: shift,
13818             ctrl: ctrl,
13819             alt: alt,
13820             fn: fn,
13821             scope: scope
13822         })
13823     },
13824
13825     // private
13826     handleKeyDown : function(e){
13827             if(this.enabled){ //just in case
13828             var b = this.bindings;
13829             for(var i = 0, len = b.length; i < len; i++){
13830                 b[i].call(this, e);
13831             }
13832             }
13833         },
13834         
13835         /**
13836          * Returns true if this KeyMap is enabled
13837          * @return {Boolean} 
13838          */
13839         isEnabled : function(){
13840             return this.enabled;  
13841         },
13842         
13843         /**
13844          * Enables this KeyMap
13845          */
13846         enable: function(){
13847                 if(!this.enabled){
13848                     this.el.on(this.eventName, this.handleKeyDown, this);
13849                     this.enabled = true;
13850                 }
13851         },
13852
13853         /**
13854          * Disable this KeyMap
13855          */
13856         disable: function(){
13857                 if(this.enabled){
13858                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13859                     this.enabled = false;
13860                 }
13861         }
13862 };/*
13863  * Based on:
13864  * Ext JS Library 1.1.1
13865  * Copyright(c) 2006-2007, Ext JS, LLC.
13866  *
13867  * Originally Released Under LGPL - original licence link has changed is not relivant.
13868  *
13869  * Fork - LGPL
13870  * <script type="text/javascript">
13871  */
13872
13873  
13874 /**
13875  * @class Roo.util.TextMetrics
13876  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13877  * wide, in pixels, a given block of text will be.
13878  * @singleton
13879  */
13880 Roo.util.TextMetrics = function(){
13881     var shared;
13882     return {
13883         /**
13884          * Measures the size of the specified text
13885          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13886          * that can affect the size of the rendered text
13887          * @param {String} text The text to measure
13888          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13889          * in order to accurately measure the text height
13890          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13891          */
13892         measure : function(el, text, fixedWidth){
13893             if(!shared){
13894                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13895             }
13896             shared.bind(el);
13897             shared.setFixedWidth(fixedWidth || 'auto');
13898             return shared.getSize(text);
13899         },
13900
13901         /**
13902          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13903          * the overhead of multiple calls to initialize the style properties on each measurement.
13904          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13905          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13906          * in order to accurately measure the text height
13907          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13908          */
13909         createInstance : function(el, fixedWidth){
13910             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13911         }
13912     };
13913 }();
13914
13915  
13916
13917 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13918     var ml = new Roo.Element(document.createElement('div'));
13919     document.body.appendChild(ml.dom);
13920     ml.position('absolute');
13921     ml.setLeftTop(-1000, -1000);
13922     ml.hide();
13923
13924     if(fixedWidth){
13925         ml.setWidth(fixedWidth);
13926     }
13927      
13928     var instance = {
13929         /**
13930          * Returns the size of the specified text based on the internal element's style and width properties
13931          * @memberOf Roo.util.TextMetrics.Instance#
13932          * @param {String} text The text to measure
13933          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13934          */
13935         getSize : function(text){
13936             ml.update(text);
13937             var s = ml.getSize();
13938             ml.update('');
13939             return s;
13940         },
13941
13942         /**
13943          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13944          * that can affect the size of the rendered text
13945          * @memberOf Roo.util.TextMetrics.Instance#
13946          * @param {String/HTMLElement} el The element, dom node or id
13947          */
13948         bind : function(el){
13949             ml.setStyle(
13950                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13951             );
13952         },
13953
13954         /**
13955          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13956          * to set a fixed width in order to accurately measure the text height.
13957          * @memberOf Roo.util.TextMetrics.Instance#
13958          * @param {Number} width The width to set on the element
13959          */
13960         setFixedWidth : function(width){
13961             ml.setWidth(width);
13962         },
13963
13964         /**
13965          * Returns the measured width of the specified text
13966          * @memberOf Roo.util.TextMetrics.Instance#
13967          * @param {String} text The text to measure
13968          * @return {Number} width The width in pixels
13969          */
13970         getWidth : function(text){
13971             ml.dom.style.width = 'auto';
13972             return this.getSize(text).width;
13973         },
13974
13975         /**
13976          * Returns the measured height of the specified text.  For multiline text, be sure to call
13977          * {@link #setFixedWidth} if necessary.
13978          * @memberOf Roo.util.TextMetrics.Instance#
13979          * @param {String} text The text to measure
13980          * @return {Number} height The height in pixels
13981          */
13982         getHeight : function(text){
13983             return this.getSize(text).height;
13984         }
13985     };
13986
13987     instance.bind(bindTo);
13988
13989     return instance;
13990 };
13991
13992 // backwards compat
13993 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13994  * Based on:
13995  * Ext JS Library 1.1.1
13996  * Copyright(c) 2006-2007, Ext JS, LLC.
13997  *
13998  * Originally Released Under LGPL - original licence link has changed is not relivant.
13999  *
14000  * Fork - LGPL
14001  * <script type="text/javascript">
14002  */
14003
14004 /**
14005  * @class Roo.state.Provider
14006  * Abstract base class for state provider implementations. This class provides methods
14007  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14008  * Provider interface.
14009  */
14010 Roo.state.Provider = function(){
14011     /**
14012      * @event statechange
14013      * Fires when a state change occurs.
14014      * @param {Provider} this This state provider
14015      * @param {String} key The state key which was changed
14016      * @param {String} value The encoded value for the state
14017      */
14018     this.addEvents({
14019         "statechange": true
14020     });
14021     this.state = {};
14022     Roo.state.Provider.superclass.constructor.call(this);
14023 };
14024 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14025     /**
14026      * Returns the current value for a key
14027      * @param {String} name The key name
14028      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14029      * @return {Mixed} The state data
14030      */
14031     get : function(name, defaultValue){
14032         return typeof this.state[name] == "undefined" ?
14033             defaultValue : this.state[name];
14034     },
14035     
14036     /**
14037      * Clears a value from the state
14038      * @param {String} name The key name
14039      */
14040     clear : function(name){
14041         delete this.state[name];
14042         this.fireEvent("statechange", this, name, null);
14043     },
14044     
14045     /**
14046      * Sets the value for a key
14047      * @param {String} name The key name
14048      * @param {Mixed} value The value to set
14049      */
14050     set : function(name, value){
14051         this.state[name] = value;
14052         this.fireEvent("statechange", this, name, value);
14053     },
14054     
14055     /**
14056      * Decodes a string previously encoded with {@link #encodeValue}.
14057      * @param {String} value The value to decode
14058      * @return {Mixed} The decoded value
14059      */
14060     decodeValue : function(cookie){
14061         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14062         var matches = re.exec(unescape(cookie));
14063         if(!matches || !matches[1]) return; // non state cookie
14064         var type = matches[1];
14065         var v = matches[2];
14066         switch(type){
14067             case "n":
14068                 return parseFloat(v);
14069             case "d":
14070                 return new Date(Date.parse(v));
14071             case "b":
14072                 return (v == "1");
14073             case "a":
14074                 var all = [];
14075                 var values = v.split("^");
14076                 for(var i = 0, len = values.length; i < len; i++){
14077                     all.push(this.decodeValue(values[i]));
14078                 }
14079                 return all;
14080            case "o":
14081                 var all = {};
14082                 var values = v.split("^");
14083                 for(var i = 0, len = values.length; i < len; i++){
14084                     var kv = values[i].split("=");
14085                     all[kv[0]] = this.decodeValue(kv[1]);
14086                 }
14087                 return all;
14088            default:
14089                 return v;
14090         }
14091     },
14092     
14093     /**
14094      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14095      * @param {Mixed} value The value to encode
14096      * @return {String} The encoded value
14097      */
14098     encodeValue : function(v){
14099         var enc;
14100         if(typeof v == "number"){
14101             enc = "n:" + v;
14102         }else if(typeof v == "boolean"){
14103             enc = "b:" + (v ? "1" : "0");
14104         }else if(v instanceof Date){
14105             enc = "d:" + v.toGMTString();
14106         }else if(v instanceof Array){
14107             var flat = "";
14108             for(var i = 0, len = v.length; i < len; i++){
14109                 flat += this.encodeValue(v[i]);
14110                 if(i != len-1) flat += "^";
14111             }
14112             enc = "a:" + flat;
14113         }else if(typeof v == "object"){
14114             var flat = "";
14115             for(var key in v){
14116                 if(typeof v[key] != "function"){
14117                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14118                 }
14119             }
14120             enc = "o:" + flat.substring(0, flat.length-1);
14121         }else{
14122             enc = "s:" + v;
14123         }
14124         return escape(enc);        
14125     }
14126 });
14127
14128 /*
14129  * Based on:
14130  * Ext JS Library 1.1.1
14131  * Copyright(c) 2006-2007, Ext JS, LLC.
14132  *
14133  * Originally Released Under LGPL - original licence link has changed is not relivant.
14134  *
14135  * Fork - LGPL
14136  * <script type="text/javascript">
14137  */
14138 /**
14139  * @class Roo.state.Manager
14140  * This is the global state manager. By default all components that are "state aware" check this class
14141  * for state information if you don't pass them a custom state provider. In order for this class
14142  * to be useful, it must be initialized with a provider when your application initializes.
14143  <pre><code>
14144 // in your initialization function
14145 init : function(){
14146    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14147    ...
14148    // supposed you have a {@link Roo.BorderLayout}
14149    var layout = new Roo.BorderLayout(...);
14150    layout.restoreState();
14151    // or a {Roo.BasicDialog}
14152    var dialog = new Roo.BasicDialog(...);
14153    dialog.restoreState();
14154  </code></pre>
14155  * @singleton
14156  */
14157 Roo.state.Manager = function(){
14158     var provider = new Roo.state.Provider();
14159     
14160     return {
14161         /**
14162          * Configures the default state provider for your application
14163          * @param {Provider} stateProvider The state provider to set
14164          */
14165         setProvider : function(stateProvider){
14166             provider = stateProvider;
14167         },
14168         
14169         /**
14170          * Returns the current value for a key
14171          * @param {String} name The key name
14172          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14173          * @return {Mixed} The state data
14174          */
14175         get : function(key, defaultValue){
14176             return provider.get(key, defaultValue);
14177         },
14178         
14179         /**
14180          * Sets the value for a key
14181          * @param {String} name The key name
14182          * @param {Mixed} value The state data
14183          */
14184          set : function(key, value){
14185             provider.set(key, value);
14186         },
14187         
14188         /**
14189          * Clears a value from the state
14190          * @param {String} name The key name
14191          */
14192         clear : function(key){
14193             provider.clear(key);
14194         },
14195         
14196         /**
14197          * Gets the currently configured state provider
14198          * @return {Provider} The state provider
14199          */
14200         getProvider : function(){
14201             return provider;
14202         }
14203     };
14204 }();
14205 /*
14206  * Based on:
14207  * Ext JS Library 1.1.1
14208  * Copyright(c) 2006-2007, Ext JS, LLC.
14209  *
14210  * Originally Released Under LGPL - original licence link has changed is not relivant.
14211  *
14212  * Fork - LGPL
14213  * <script type="text/javascript">
14214  */
14215 /**
14216  * @class Roo.state.CookieProvider
14217  * @extends Roo.state.Provider
14218  * The default Provider implementation which saves state via cookies.
14219  * <br />Usage:
14220  <pre><code>
14221    var cp = new Roo.state.CookieProvider({
14222        path: "/cgi-bin/",
14223        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14224        domain: "roojs.com"
14225    })
14226    Roo.state.Manager.setProvider(cp);
14227  </code></pre>
14228  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14229  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14230  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14231  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14232  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14233  * domain the page is running on including the 'www' like 'www.roojs.com')
14234  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14235  * @constructor
14236  * Create a new CookieProvider
14237  * @param {Object} config The configuration object
14238  */
14239 Roo.state.CookieProvider = function(config){
14240     Roo.state.CookieProvider.superclass.constructor.call(this);
14241     this.path = "/";
14242     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14243     this.domain = null;
14244     this.secure = false;
14245     Roo.apply(this, config);
14246     this.state = this.readCookies();
14247 };
14248
14249 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14250     // private
14251     set : function(name, value){
14252         if(typeof value == "undefined" || value === null){
14253             this.clear(name);
14254             return;
14255         }
14256         this.setCookie(name, value);
14257         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14258     },
14259
14260     // private
14261     clear : function(name){
14262         this.clearCookie(name);
14263         Roo.state.CookieProvider.superclass.clear.call(this, name);
14264     },
14265
14266     // private
14267     readCookies : function(){
14268         var cookies = {};
14269         var c = document.cookie + ";";
14270         var re = /\s?(.*?)=(.*?);/g;
14271         var matches;
14272         while((matches = re.exec(c)) != null){
14273             var name = matches[1];
14274             var value = matches[2];
14275             if(name && name.substring(0,3) == "ys-"){
14276                 cookies[name.substr(3)] = this.decodeValue(value);
14277             }
14278         }
14279         return cookies;
14280     },
14281
14282     // private
14283     setCookie : function(name, value){
14284         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14285            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14286            ((this.path == null) ? "" : ("; path=" + this.path)) +
14287            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14288            ((this.secure == true) ? "; secure" : "");
14289     },
14290
14291     // private
14292     clearCookie : function(name){
14293         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14294            ((this.path == null) ? "" : ("; path=" + this.path)) +
14295            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14296            ((this.secure == true) ? "; secure" : "");
14297     }
14298 });